import datetime
import json
import logging
import re
import time
import uuid
from decimal import Decimal

import pymongo
import xmltodict
from sqlalchemy import and_, extract, func
from tornado import httpclient
from tornado.escape import json_encode
from tornado.httpclient import HTTPRequest
from tornado.httputil import url_concat

from base.connection import mongodb_util, redis_util
from base.request import ApiBaseHandler
from common.definition import (OrderStatus, OrderType, PayStatus, PayType, ret_msg)
from conf import config
from conf.config import DOMAIN, KUAIDI100_CUSTOMER
from daemon import tasks
from helper.api_helper import ApiHelper
from helper.beanstalkd import add_mq_task
from helper.coupon_helper import get_available_coupons
from helper.express_helper import ExpressHelper
from helper.meituan.cancel_order import CancelOrder
from helper.meituan.check_order import CheckOrder
from helper.meituan.create_shop_order import CreateOrderByShop
from helper.meituan.query_order import QueryOrder
from helper.meituan.query_shop_area import QueryShopArea
from helper.meituan.rider_location import GetRiderLocation
from helper.meituan.sign_helper import SignHelper
from helper.mysql_help import get_store_info
from helper.payment.balance import (BalanceHandler, create_balance_record,
                                    update_account_money)
from helper.payment.pay_util import calculate_order_fee, generator_trade_no
from helper.payment.refund import cancel_order_immediate_refund
from helper.payment.wechat_base import Wechat_Base
from helper.payment.wechat_pay import WechatPay, weixin_verify
from helper.payment.weixin_xml import wechat_resp
from helper.user import AddressHelper, UserHelper
from util.json_util import JSONEncoder

logger = logging.getLogger("wechat.handlers.store")


class Mall(ApiBaseHandler):
    async def get(self):
        open_id = await self.get_open_id()
        # 通用参数
        page_index = int(self.get_parameter("page_index", 0))
        page_size = int(self.get_parameter("page_size", 10))
        store_id = int(self.get_parameter("store_id", 0))
        room_id = self.get_parameter("room_id", "0")

        lng = float(self.get_parameter("lng", 0))
        lat = float(self.get_parameter("lat", 0))
        # 检查参数的合法性
        ApiHelper.inspect_common_params(page_index, page_size)
        page_start = page_index * page_size
        if not store_id:
            # 默认为线上门店
            store_id = UserHelper.get_online_store_id(open_id)

        class Goods(object):
            pass

        class Carts(object):
            pass

        class User(object):
            pass

        class Agency(object):
            pass

        class UserAgency(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_store_goods", "model": Goods},
            {"db_table": "sdet_store_cart", "model": Carts},
            {"db_table": "ebt_user", "model": User},
            {"db_table": "ebt_agency", "model": Agency},
            {"db_table": "ebt_user_agency", "model": UserAgency}
        ])
        filter = dict(
            sdef_goods_status=1,
            sdef_goods_is_visible=1,
            sdef_goods_is_delete=0,
            sdef_goods_store_id=store_id,
            sdef_goods_factory_uid=config.FACTORY_UID
        )
        goods_array = session.query(Goods).filter_by(
            **filter).order_by(
            Goods.sdef_goods_order_num.asc(),
            Goods.sdef_goods_price.asc())[page_start:page_start + page_size]

        cart = session.query(Carts).filter_by(sdef_cart_device_id=open_id, sdef_cart_store_id=store_id).first()

        # 购物车商品数目
        cart_count = 0
        if cart:
            cart_goods = json.loads(cart.sdef_cart_goods)
            for g in cart_goods:
                cart_count += g["count"]

        agency = session.query(Agency).filter_by(ebf_agency_id=store_id).first()
        if not agency:
            session.close()
            return self.write(ret_msg(code=-1, error_message="门店%s不存在！" % store_id))

        name = agency.ebf_agency_name
        address = agency.ebf_agency_address
        phone = agency.ebf_agency_contact
        store_img = self.get_store_logo(store_id, agency)
        store_lng = agency.ebf_agency_longitude if agency.ebf_agency_longitude else 0
        store_lat = agency.ebf_agency_latitude if agency.ebf_agency_latitude else 0

        goods_list = []
        if goods_array:
            for g in goods_array:
                goods = {}
                goods["goods_id"] = g.sdef_goods_id
                goods["goods_thumbnail"] = json.loads(g.sdef_goods_thumbnail)
                goods["goods_name"] = g.sdef_goods_name
                goods["goods_price"] = str(g.sdef_goods_price.quantize(Decimal("0.00")))
                goods["goods_member_price"] = str(g.sdef_goods_member_price.quantize(Decimal("0.00")))
                goods["goods_count"] = g.sdef_goods_count
                goods["unit"] = ApiHelper.get_unit(g.sdef_goods_unit)
                goods["pin_price"] = "%.2f" % g.sdef_goods_pin_price if g.sdef_goods_pin_price else None
                goods["list_price"] = "%.2f" % g.sdef_goods_list_price if g.sdef_goods_list_price else None
                goods["mpq"] = g.sdef_goods_mpq
                goods["video"] = g.sdef_goods_video
                goods_list.append(goods)

        context = dict(
            goods_list=goods_list,
            cart_count=cart_count,
            is_membership=self.is_membership(open_id),
            store_id=store_id,
            store_name=name,
            address=address,
            phone=phone,
            store_img=store_img,
            room_id=room_id,
            distance=ApiHelper.geodistance(lng, lat, float(store_lng), float(store_lat))
        )

        session.close()
        return self.write(ret_msg(data=context))

    async def post(self):
        open_id = await self.get_open_id()
        action = self.get_parameter("action", None)
        store_id = int(self.get_parameter("store_id", 0))
        room_id = self.get_parameter("room_id", "0")
        cart_goods = []
        if not store_id:
            return self.write(ret_msg(code=-1, error_message="请求参数缺少门店编号！"))

        if action == "add-cart":
            goods_id = self.get_parameter("goods_id", None)
            count = int(self.get_parameter("count", 1))

            if not (goods_id and re.match("^\d+$", goods_id)):
                return self.write(ret_msg(-1, error_message="商品编号错误"))

            class Carts(object):
                pass

            class Goods(object):
                pass

            session = self.get_db_session([
                {"db_table": "sdet_store_cart", "model": Carts},
                {"db_table": "sdet_store_goods", "model": Goods},
            ])

            # 检查库存：必须是上架+未删除+库存大于0
            filter = dict(
                sdef_goods_id=goods_id,
                sdef_goods_status=1,
                sdef_goods_is_delete=0,
                sdef_goods_store_id=store_id,
                sdef_goods_factory_uid=config.FACTORY_UID
            )
            goods = session.query(Goods).filter_by(**filter).first()

            if not goods:
                session.close()
                return self.write(ret_msg(-1, error_message="商品已下架"))

            if goods.sdef_goods_count < 1:
                session.close()
                return self.write(ret_msg(-1, error_message="商品无库存"))

            # 检查数量是否超出库存
            if count > goods.sdef_goods_count:
                session.close()
                return self.write(ret_msg(-1, error_message="商品库存不足"))

            logger.info("store_id=%s" % store_id)
            cart = session.query(Carts).filter_by(sdef_cart_device_id=open_id, sdef_cart_store_id=store_id).first()
            is_exists = False
            if cart:
                is_exists = True
                cart_goods = json.loads(cart.sdef_cart_goods)

                # 检查购物车是否已经有这个商品
                for c in cart_goods:
                    if c.get("id") == goods_id:
                        c["count"] += count
                        if c["count"] > goods.sdef_goods_count:
                            session.close()
                            return self.write(ret_msg(-1, error_message="商品库存不足"))

                        c["is_buy"] = 1
                        # 修改
                        cart_goods_json = json.dumps(cart_goods)
                        session.query(Carts).filter_by(
                            sdef_cart_device_id=open_id,
                            sdef_cart_store_id=store_id
                        ).update({Carts.sdef_cart_goods: cart_goods_json})

                        session.commit()
                        # 计算购物车商品数量
                        cart_count = 0
                        for c in cart_goods:
                            cart_count += c["count"]

                        session.close()
                        return self.write(ret_msg(data=cart_count))

            # 新商品或者不同店同种商品添加进购物车
            cart_goods.append({"id": goods_id, "count": count, "store_id": store_id, "room_id": room_id, "is_buy": 1})
            cart_goods_json = json.dumps(cart_goods)
            if is_exists:
                # 修改
                session.query(Carts).filter_by(
                    sdef_cart_device_id=open_id,
                    sdef_cart_store_id=store_id
                ).update({Carts.sdef_cart_goods: cart_goods_json})
                session.commit()
            else:
                # 添加购物车
                _cart = Carts()
                _cart.sdef_cart_device_id = open_id
                _cart.sdef_cart_goods = cart_goods_json
                _cart.sdef_cart_store_id = store_id
                _cart.sdef_cart_update_time = datetime.datetime.now()
                session.add(_cart)
                session.commit()

        # 计算购物车商品数量
        cart_count = 0
        for c in cart_goods:
            cart_count += c["count"]

        session.close()
        return self.write(ret_msg(data=cart_count))


class StoreList(ApiBaseHandler):
    async def get(self):
        class Agency(object):
            pass

        session = self.get_db_session([
            {"db_table": "ebt_agency", "model": Agency},
        ])

        content = self.get_parameter("content", None)
        lng = float(self.get_parameter("lng", 0))
        lat = float(self.get_parameter("lat", 0))
        pick_up = int(self.get_parameter("pick_up", 0))  # 0=店内点酒 1=礼品订单门店自提 2=买单送I款的门店 3=具有酒杯酒且库存不小于1的门店按距离由近及远排序
        sub_type = int(self.get_parameter("sub_type", 3))  # 0=普通门店 1=高级门店 2=行家 3=运营中心
        page_index = int(self.get_parameter("page_index", 0))
        page_size = int(self.get_parameter("page_size", 10))
        page_start = page_index * page_size
        img_default = f"https://{DOMAIN}/static/img/content/logo-60x60.png"

        store_info = []
        items = get_store_info(lng, lat, page_start, page_size, content, pick_up, sub_type)
        for i in items:
            # 过滤没有定位的门店
            distance = i.get("distance")
            if distance == "null":
                continue

            agency_id = i["id"]
            address = i["address"]
            work_time = i["work_time"]
            work_switch = i["active"]
            headurl = i["headurl"]
            if not work_time:
                work_time = '{"day": [1, 2, 3, 4, 5, 6, 7], "am": "09:30-14:00", "pm": "14:00-24:00"}'

            work_time = json.loads(work_time)
            is_work = ApiHelper.check_is_work_time(work_time, work_switch)
            store = dict(
                address=address,
                store_id=agency_id,
                is_work=is_work,
                work_time=work_time,
                goods_count=i.get("goods_count", 0),
                phone=i["phone"],
                lng=i["lng"],
                lat=i["lat"],
                store_name=i["name"],
                distance=i.get("distance", 0),
                store_img=headurl if headurl and "logo-60x60.png" not in headurl else img_default
            )
            store_info.append(store)
        session.close()
        return self.write(ret_msg(data=store_info))


class Cart(ApiBaseHandler):
    async def get(self):
        open_id = await self.get_open_id()
        is_membership = self.is_membership(open_id)
        logger.info("是否为会员: open_id=%s,is_membership=%s" % (open_id, is_membership))
        store_id = int(self.get_parameter("store_id", 0))
        if not store_id:
            return self.write(ret_msg(code=-1, error_message="请求参数缺少门店编号！"))

        class Goods(object):
            pass

        class Carts(object):
            pass

        class UserAddress(object):
            pass

        class Agency(object):
            pass

        class UserAgency(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_store_goods", "model": Goods},
            {"db_table": "sdet_store_cart", "model": Carts},
            {"db_table": "sdet_address", "model": UserAddress},
            {"db_table": "ebt_agency", "model": Agency},
            {"db_table": "ebt_user_agency", "model": UserAgency}
        ])

        cart = session.query(Carts).filter_by(sdef_cart_device_id=open_id, sdef_cart_store_id=store_id).first()
        cart_goods_array = []
        cart_goods = []
        cart_count = 0
        price_total = Decimal(0)
        filters = dict(
            sdef_goods_status=1,
            sdef_goods_is_delete=0,
            sdef_goods_store_id=store_id,
            sdef_goods_factory_uid=config.FACTORY_UID
        )
        agency = session.query(Agency).filter_by(ebf_agency_type=3, ebf_agency_id=store_id).first()
        if not agency:
            session.close()
            return self.write(ret_msg(code=-1, error_message="门店%s不存在！" % store_id))

        if cart:
            cart_goods = json.loads(cart.sdef_cart_goods)
            # 清空购物车中已下架或没有库存的商品
            id_list = list()
            for c in cart_goods:
                id = c["id"]
                g = session.query(Goods).filter(
                    Goods.sdef_goods_count > 0
                ).filter_by(**filters).filter_by(sdef_goods_id=id).first()
                if not g:
                    id_list.append(id)

            if id_list:
                logger.info("id_list=%s" % id_list)
                cart_goods = list(filter(lambda x: x['id'] not in id_list, cart_goods))
                if cart_goods:
                    session.query(Carts).filter_by(
                        sdef_cart_device_id=open_id,
                        sdef_cart_store_id=store_id
                    ).update({Carts.sdef_cart_goods: json.dumps(cart_goods)})
                else:
                    ret = session.query(Carts).filter_by(sdef_cart_device_id=open_id,
                                                         sdef_cart_store_id=store_id).delete()
                    logger.info("删除购物车: delete cart ret=%s" % ret)
                session.commit()

        for c in cart_goods:
            goods_id = c["id"]
            count = c["count"]
            cart_count += count
            is_buy = c.get("is_buy", 0)
            _goods = session.query(Goods).filter_by(sdef_goods_id=goods_id).filter_by(**filters).first()
            goods_price = _goods.sdef_goods_member_price if is_membership else _goods.sdef_goods_price
            mpq = _goods.sdef_goods_mpq
            goods = dict(
                id=goods_id,
                count=count,
                is_buy=is_buy,
                goods_img=json.loads(_goods.sdef_goods_thumbnail)[0],
                goods_name=_goods.sdef_goods_name,
                goods_price=str(goods_price.quantize(Decimal("0.00"))),
                mpq=mpq,
            )
            if is_buy:
                price_total += goods_price * Decimal(count)

            cart_goods_array.append(goods)

        default_address = AddressHelper.get_default(open_id)
        use_address = AddressHelper.get_use(open_id)
        db = mongodb_util.db
        address = None

        if use_address:
            logger.debug("使用地址，address_id=%s" % use_address["address_id"])
            address = session.query(UserAddress).filter_by(sdef_address_id=use_address["address_id"]).first()
        elif default_address:
            logger.debug("默认地址，address_id=%s" % default_address["address_id"])
            address = session.query(UserAddress).filter_by(sdef_address_id=default_address["address_id"]).first()
        else:
            result = db.sdec_address.find(
                {"user_id": open_id, "is_delete": False}
            ).sort("update_time", pymongo.DESCENDING)

            for a in result:
                address = session.query(UserAddress).filter_by(sdef_address_id=a["address_id"]).first()
                break

        if address:
            address_dict = dict(
                province=address.sdef_address_province,
                city=address.sdef_address_city,
                county=address.sdef_address_county,
                detail=address.sdef_address_detail,
                name=address.sdef_address_name,
                phone=address.sdef_address_phone
            )

        context = dict(
            cart_goods_array=cart_goods_array,
            price_total=str(price_total.quantize(Decimal("0.00"))),
            address=address_dict if address else {},
            love_goods=self.love_goods(open_id, store_id),
            store_id=store_id,
            store_address=agency.ebf_agency_address,
            store_name=agency.ebf_agency_name,
            cart_count=cart_count
        )
        session.close()
        return self.finish(ret_msg(data=context))

    async def post(self):
        class Carts(object):
            pass

        class Goods(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_store_cart", "model": Carts},
            {"db_table": "sdet_store_goods", "model": Goods},
        ])

        open_id = await self.get_open_id()
        action = self.get_parameter("action", None)
        store_id = int(self.get_parameter("store_id", 0))
        cart = session.query(Carts).filter_by(sdef_cart_device_id=open_id, sdef_cart_store_id=store_id).first()
        if not store_id:
            return self.write(ret_msg(code=-1, error_message="请求参数缺少门店编号！"))

        if cart:
            cart_goods = json.loads(cart.sdef_cart_goods)
        else:
            cart_goods = []

        if action == "mod":
            goods_id = self.get_parameter("goods_id")
            count = int(self.get_parameter("count", 0))
            if count <= 0:
                session.close()
                return self.write(ret_msg(-1, error_message="商品数量必须大于0"))

            filter = dict(
                sdef_goods_status=1,
                sdef_goods_is_delete=0,
                sdef_goods_store_id=store_id,
                sdef_goods_id=goods_id,
                sdef_goods_factory_uid=config.FACTORY_UID
            )
            goods = session.query(Goods).filter_by(**filter).first()

            if not goods:
                session.close()
                return self.write(ret_msg(-1, error_message="商品已下架"))

            if goods.sdef_goods_count < 1:
                session.close()
                return self.write(ret_msg(-1, error_message="商品库存不足"))

            # 检查数量是否超出库存
            if count > goods.sdef_goods_count:
                session.close()
                return self.write(ret_msg(-1, error_message="%s库存不足" % goods.sdef_goods_name))

            for c in cart_goods:
                if c.get("id") == goods_id:
                    c["count"] = int(count)
                    c["is_buy"] = 1
                    session.query(Carts).filter_by(
                        sdef_cart_device_id=open_id,
                        sdef_cart_store_id=store_id
                    ).update({Carts.sdef_cart_goods: json.dumps(cart_goods)})

                    # 计算购物车商品数量
                    cart_count = 0
                    for c1 in cart_goods:
                        cart_count += c1["count"]

                    session.commit()
                    session.close()
                    return self.write(ret_msg(data=cart_count))

            session.close()
            return self.write(ret_msg(-1, error_message="购物车不存在此商品"))
        elif action == "del":
            id_list = self.get_parameter("goods_id")  # 门店编号与商品编号组成的列表
            if not isinstance(id_list, list):
                session.close()
                return self.write(ret_msg(-1, error_message="请求参数类型错误！"))

            cart_goods_new = []
            for c in cart_goods:
                flag = False
                for id in id_list:
                    if c["id"] == id:
                        flag = True
                        break

                if not flag:
                    cart_goods_new.append(c)

            if not cart_goods_new:
                ret = session.query(Carts).filter_by(sdef_cart_device_id=open_id, sdef_cart_store_id=store_id).delete()
                logger.info("delete cart ret=%s" % ret)
            else:
                session.query(Carts).filter_by(
                    sdef_cart_device_id=open_id,
                    sdef_cart_store_id=store_id
                ).update({Carts.sdef_cart_goods: json.dumps(cart_goods_new)})

            session.commit()
            session.close()
            return self.write(ret_msg())

        return self.write(ret_msg())


class BuyPartGoods(ApiBaseHandler):
    async def post(self):
        id_array = self.get_parameter("id_array", [])
        store_id = int(self.get_parameter("store_id", 0))
        logger.info("id_array: %s, type(id_array): %s" % (id_array, type(id_array)))
        if not store_id:
            return self.write(ret_msg(code=-1, error_message="请求参数缺少门店编号！"))

        if not id_array:
            return self.write(ret_msg(code=-1, error_message="请选择要购买的商品！"))

        if not isinstance(id_array, list):
            return self.write(ret_msg(code=-1, error_message="请求参数错误！"))

        class Carts(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_store_cart", "model": Carts}
        ])

        open_id = await self.get_open_id()
        cart = session.query(Carts).filter_by(sdef_cart_device_id=open_id, sdef_cart_store_id=store_id).first()
        if not cart:
            session.close()
            return self.write(ret_msg(code=-1, error_message="用户购物车不存在！"))

        cart_goods = json.loads(cart.sdef_cart_goods)
        if not cart_goods:
            session.close()
            return self.write(ret_msg(code=-1, error_message="购物车中没有商品！"))

        for c in cart_goods:
            if c["id"] in id_array:
                c["is_buy"] = 1
            else:
                c["is_buy"] = 0

        cart_goods_json = json.dumps(cart_goods)
        session.query(Carts).filter_by(
            sdef_cart_device_id=open_id,
            sdef_cart_store_id=store_id
        ).update({Carts.sdef_cart_goods: cart_goods_json})
        session.commit()
        session.close()
        return self.write(ret_msg())


class GoodsDetail(ApiBaseHandler):
    async def get(self, goods_id):
        class Goods(object):
            pass

        class UserAddress(object):
            pass

        class Carts(object):
            pass

        class User(object):
            pass

        class Agency(object):
            pass

        class UserAgency(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_store_goods", "model": Goods},
            {"db_table": "sdet_address", "model": UserAddress},
            {"db_table": "sdet_store_cart", "model": Carts},
            {"db_table": "ebt_user", "model": User},
            {"db_table": "ebt_agency", "model": Agency},
            {"db_table": "ebt_user_agency", "model": UserAgency}
        ])

        open_id = await self.get_open_id()
        store_id = int(self.get_parameter("store_id", 0))
        room_id = self.get_parameter("room_id", "0")
        if not store_id:
            return self.write(ret_msg(code=-1, error_message="请求参数缺少门店编号！"))

        filter = dict(
            sdef_goods_status=1,
            sdef_goods_is_delete=0,
            sdef_goods_store_id=store_id,
            sdef_goods_id=goods_id,
            sdef_goods_factory_uid=config.FACTORY_UID
        )
        _goods = session.query(Goods).filter_by(**filter).first()
        agency = session.query(Agency).filter_by(ebf_agency_type=3, ebf_agency_id=store_id).first()
        cart = session.query(Carts).filter_by(sdef_cart_device_id=open_id, sdef_cart_store_id=store_id).first()
        cart_count = 0

        if not _goods:
            session.close()
            return self.write(ret_msg(code=-1, error_message="商品已下架！"))

        if cart:
            cart_goods = json.loads(cart.sdef_cart_goods)
            for g in cart_goods:
                cart_count += g["count"]

        if not agency:
            session.close()
            return self.write(ret_msg(code=-1, error_message="门店%s不存在！" % store_id))

        agency_id = agency.ebf_agency_id
        default_address = AddressHelper.get_default(open_id)
        use_address = AddressHelper.get_use(open_id)
        db = mongodb_util.db
        address = None

        if use_address:
            logger.debug("使用地址，address_id=%s" % use_address["address_id"])
            address = session.query(UserAddress).filter_by(sdef_address_id=use_address["address_id"]).first()
        elif default_address:
            address = session.query(UserAddress).filter_by(sdef_address_id=default_address["address_id"]).first()
        else:
            result = db.sdec_address.find({"user_id": open_id, "is_delete": False}).sort("update_time",
                                                                                         pymongo.DESCENDING)
            for a in result:
                address = session.query(UserAddress).filter_by(sdef_address_id=a["address_id"]).first()
                break

        if address:
            address_dict = dict(
                province=address.sdef_address_province,
                city=address.sdef_address_city,
                county=address.sdef_address_county,
                detail=address.sdef_address_detail,
                name=address.sdef_address_name,
                phone=address.sdef_address_phone
            )

        goods_dict = dict(
            goods_name=_goods.sdef_goods_name,
            goods_describe=_goods.sdef_goods_describe,
            goods_price=str(_goods.sdef_goods_price.quantize(Decimal("0.00"))),
            goods_member_price=str(_goods.sdef_goods_member_price.quantize(Decimal("0.00"))),
            goods_id=_goods.sdef_goods_id,
            goods_count=_goods.sdef_goods_count,
            goods_img_intro=json.loads(_goods.sdef_goods_img_intro),
            goods_thumbnail=json.loads(_goods.sdef_goods_thumbnail),
            goods_imgs=json.loads(_goods.sdef_goods_img),
            pin_price="%.2f" % _goods.sdef_goods_pin_price if _goods.sdef_goods_pin_price else None,
            list_price="%.2f" % _goods.sdef_goods_list_price if _goods.sdef_goods_list_price else None,
            unit=ApiHelper.get_unit(_goods.sdef_goods_unit),
            mpq=_goods.sdef_goods_mpq,
            video=_goods.sdef_goods_video,
            goods_sold=_goods.sdef_goods_sold,
            balance_rate=_goods.sdef_goods_balance_rate,
        )
        context = dict(
            goods=goods_dict,
            address=address_dict if address else {},
            is_membership=self.is_membership(open_id),
            store_id=store_id,
            room_id=room_id,
            cart_count=cart_count,
            store_addr=agency.ebf_agency_address,
            store_name=agency.ebf_agency_name,
            store_img=self.get_store_logo(agency_id, agency)
        )
        self.add_love_goods(open_id, goods_id)
        session.close()
        return self.write(ret_msg(data=context))


class PayConfirm(ApiBaseHandler):
    def create_coupon_order(self, open_id, cu_id, order_id, store_id, price_total, coupon_fee, wechat_fee, balance_fee):
        """
        创建优惠券消费记录
        :param open_id:
        :param cu_id:
        :param order_id:
        :param price_total:
        :param coupon_fee:
        :param wechat_fee:
        :param balance_fee:
        :return:
        """
        class CouponUser(object):
            pass

        class Coupon(object):
            pass

        class CouponOrder(object):
            pass

        session = self.get_db_session([
            {"db_table": "ebt_coupon", "model": Coupon},
            {"db_table": "ebt_coupon_user", "model": CouponUser},
            {"db_table": "ebt_coupon_order", "model": CouponOrder}
        ])

        filters1 = dict(
            user_id=open_id,
            coupon_user_id=cu_id,
            status=0
        )
        old_co = session.query(CouponOrder).filter_by(**filters1).first()
        if old_co:
            old_order_id = old_co.order_id
            old_co.order_id = order_id
            logger.info(f"修改优惠券消费记录成功: open_id={open_id} cu_id={cu_id} old_order_id={old_order_id}")
            session.commit()
            session.close()
            return True

        co = CouponOrder()
        co.user_id = open_id
        co.store_id = store_id
        co.coupon_user_id = cu_id
        co.order_id = order_id
        co.order_total = price_total
        co.money = coupon_fee
        co.actual_paid = wechat_fee
        co.paid_balance = balance_fee
        co.create_time = datetime.datetime.now()
        co.status = 0
        session.add(co)
        session.commit()
        session.close()
        logger.info(f"创建优惠券使用记录成功order_id={order_id}, oid={open_id}, cu_id={cu_id}")
        return True

    def update_coupons_status(self, cu_id, open_id, order_id):
        class CouponUser(object):
            pass

        class CouponOrder(object):
            pass

        session = self.get_db_session([
            {"db_table": "ebt_coupon_user", "model": CouponUser},
            {"db_table": "ebt_coupon_order", "model": CouponOrder},
        ])

        filters = dict(
            user_id=open_id,
            order_id=order_id,
            status=0,
            coupon_user_id=cu_id
        )
        co = session.query(CouponOrder).filter_by(**filters).first()
        cu = session.query(CouponUser).filter_by(id=cu_id).first()
        if not co or not cu:
            raise Exception

        co.status = 1
        cu.status = 1
        session.commit()
        session.close()
        return True

    async def create_meituan_order(self, order_id, shop_id, name, addr, phone, lng, lat, weight, goods, fee, poi_seq):
        o = CreateOrderByShop(order_id, shop_id, name, addr, phone, lng, lat, poi_seq, weight, goods, fee)
        result = o.create_shop_order()
        return result

    async def get(self):
        open_id = await self.get_open_id()
        t = self.get_parameter("t", None)
        store_id = int(self.get_parameter("store_id", 0))
        room_id = self.get_parameter("room_id", "0")
        old_order_id = None
        message = None
        is_membership = self.is_membership(open_id)
        if not store_id:
            return self.write(ret_msg(code=-1, error_message="请求参数缺少门店编号！"))

        class Goods(object):
            pass

        class GoodsOrder(object):
            pass

        class Carts(object):
            pass

        class UserAddress(object):
            pass

        class AccountMoney(object):
            pass

        class User(object):
            pass

        class Agency(object):
            pass

        class UserAgency(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_store_goods", "model": Goods},
            {"db_table": "sdet_store_cart", "model": Carts},
            {"db_table": "sdet_address", "model": UserAddress},
            {"db_table": "sdet_order", "model": GoodsOrder},
            {"db_table": "ebt_account_money", "model": AccountMoney},
            {"db_table": "ebt_user", "model": User},
            {"db_table": "ebt_agency", "model": Agency},
            {"db_table": "ebt_user_agency", "model": UserAgency}
        ])

        user = session.query(User).filter_by(ebf_user_id=open_id).first()
        if user:
            nickname = user.ebf_user_nickname
            if nickname == "未关注" or nickname == "" or nickname is None:
                await self.update_nickname(open_id)

        agency = session.query(Agency).filter_by(ebf_agency_type=3, ebf_agency_id=store_id).first()
        if not agency:
            session.close()
            return self.write(ret_msg(code=-1, error_message="门店%s不存在！" % store_id))

        work_time = agency.ebf_agency_work_time
        if not work_time:
            work_time = '{"day": [1, 2, 3, 4, 5, 6, 7], "am": "09:30-14:00", "pm": "14:00-23:59"}'

        work_time = json.loads(work_time)
        work_switch = agency.ebf_agency_is_active
        is_work = ApiHelper.check_is_work_time(work_time, work_switch)
        cart_goods_array = []
        cart_goods = []
        goods_list = []
        price_total = Decimal(0)
        balance_fee = Decimal(0)
        coupon_fee = Decimal(0)
        forbid_balance_fee = Decimal(0)  # 禁止使用余额的商品金额

        if not is_work:
            session.close()
            return self.write(ret_msg(code=-1, error_message="门店已打烊不支持下单！"))

        if t == "buy":  # t=buy表示立刻购买,商品详情页的立即购买处理
            goods_id = self.get_parameter("id")
            count = int(self.get_parameter("c", 1))
            cart_goods.append({"id": goods_id, "count": count, "store_id": store_id, "room_id": room_id, "is_buy": 1})
        elif t == "pickup_in_store":  # t=pickup_in_store 表示店内自提（由线上平台订单转为店内自提订单）
            store_goods_list = self.get_parameter("store_goods_list", None)
            if not store_goods_list:
                session.close()
                return self.write(ret_msg(code=-1, error_message="缺店内自提商品列表参数！"))
            store_goods_list = json.loads(store_goods_list)
            for g in store_goods_list:
                cart_goods.append({"id": g["id"], "count": g["count"], "store_id": store_id, "room_id": room_id, "is_buy": 1})
        elif t == "retry":  # 继续支付
            old_order_id = self.get_parameter("oid", None)
            filters = dict(
                sdef_order_device_id=open_id,
                sdef_order_id=old_order_id,
                sdef_order_is_delete=0
            )
            old_order = session.query(GoodsOrder).filter_by(**filters).first()

            if not old_order:
                session.close()
                return self.write(ret_msg(code=-1, error_message="订单不存在！"))

            if old_order.sdef_order_status != 1:
                session.close()
                return self.write(ret_msg(code=-1, error_message="只有处于待支付的订单才能继续支付"))

            # 继续支付时解除旧订单锁定的余额
            if old_order.sdef_order_balance_fee > Decimal("0"):
                redis = redis_util.client
                lock_balance_fee = redis.get("balance_lock_%s" % open_id)
                if lock_balance_fee:
                    lock_balance_fee = Decimal(lock_balance_fee.decode())
                    lock_balance_fee -= old_order.sdef_order_balance_fee
                    if lock_balance_fee > Decimal("0"):
                        redis.setex("balance_lock_%s" % open_id, 600, str(lock_balance_fee))
                    else:
                        redis.delete("balance_lock_%s" % open_id)

            message = old_order.sdef_order_message
            cart_goods = json.loads(old_order.sdef_order_goods)
        elif t == "buy_again":  # 再次购买
            old_order_id = self.get_parameter("oid", None)
            old_order = session.query(GoodsOrder).filter_by(sdef_order_device_id=open_id,
                                                            sdef_order_id=old_order_id,
                                                            sdef_order_is_delete=0).first()
            if not old_order:
                session.close()
                return self.write(ret_msg(code=-1, error_message="订单不存在！"))

            old_fee = old_order.sdef_order_balance_fee + old_order.sdef_order_fee + old_order.sdef_order_coupon_fee
            if old_fee <= Decimal("0") or old_order.sdef_order_type != 1:
                session.close()
                return self.write(ret_msg(code=-1, error_message="只有普通订单才能再次购买！"))

            cart_goods = json.loads(old_order.sdef_order_goods)
        else:
            cart = session.query(Carts).filter_by(sdef_cart_device_id=open_id, sdef_cart_store_id=store_id).first()
            if not cart:
                session.close()
                return self.write(ret_msg(code=-1, error_message="请求参数错误！"))

            old_cart_goods = json.loads(cart.sdef_cart_goods)
            for c in old_cart_goods:
                if c.get("is_buy", None):
                    cart_goods.append(c)

        for c in cart_goods:
            goods_id = c["id"]
            count = c["count"]
            is_buy = c.get("is_buy", 1)
            filters = dict(
                sdef_goods_store_id=store_id,
                sdef_goods_id=goods_id,
                sdef_goods_factory_uid=config.FACTORY_UID
            )
            _goods = session.query(Goods).filter_by(**filters).first()

            if not _goods:
                logger.error(f"商品不存在： goods_id={goods_id}")
                continue

            goods_price = _goods.sdef_goods_member_price if is_membership else _goods.sdef_goods_price
            goods = dict(
                id=goods_id,
                count=count,
                is_buy=is_buy,
                balance_rate=_goods.sdef_goods_balance_rate,
                goods_img=json.loads(_goods.sdef_goods_thumbnail)[0],
                goods_name=_goods.sdef_goods_name,
                goods_price=str(goods_price.quantize(Decimal("0.00"))),
                mpq=_goods.sdef_goods_mpq,
            )
            goods_dict = dict(
                id=goods_id,
                count=count,
                store_id=store_id,
                is_buy=is_buy,
                room_id=c.get("room_id", "0")
            )
            balance_fee += Decimal(_goods.sdef_goods_balance_rate) * Decimal(count) * goods_price
            price_total += goods_price * Decimal(count)
            cart_goods_array.append(goods)
            goods_list.append(goods_dict)
            if _goods.sdef_goods_balance_rate == 0:
                forbid_balance_fee += goods_price * Decimal(count)

        max_balance_fee = balance_fee
        default_address = AddressHelper.get_default(open_id)
        use_address = AddressHelper.get_use(open_id)
        db = mongodb_util.db
        address = None
        if use_address:
            logger.debug("使用地址: address_id=%s" % use_address["address_id"])
            address = session.query(UserAddress).filter_by(sdef_address_id=use_address["address_id"]).first()
        elif default_address:
            logger.debug("默认地址: address_id=%s" % default_address["address_id"])
            address = session.query(UserAddress).filter_by(sdef_address_id=default_address["address_id"]).first()
        else:
            result = db.sdec_address.find({"user_id": open_id, "is_delete": False}).sort("update_time",
                                                                                         pymongo.DESCENDING)
            for a in result:
                address = session.query(UserAddress).filter_by(sdef_address_id=a["address_id"]).first()
                break

        if not address:
            address_info = {}
        else:
            address_info = dict(
                province=address.sdef_address_province,
                city=address.sdef_address_city,
                county=address.sdef_address_county,
                detail=address.sdef_address_detail,
                name=address.sdef_address_name,
                phone=address.sdef_address_phone
            )

        # 获取账户余额
        enable_account_money = Decimal(0)
        account_money = session.query(AccountMoney).filter_by(ebf_am_user_id=open_id).first()
        if account_money:
            # 钱包余额需要减去redis缓存中暂扣的金额
            enable_account_money = account_money.ebf_am_money
            redis = redis_util.client
            lock_balance_fee = redis.get("balance_lock_%s" % open_id)
            if lock_balance_fee:
                enable_account_money = Decimal(enable_account_money) - Decimal(lock_balance_fee.decode())

            if account_money.ebf_am_is_forbid == 0 and enable_account_money > 0:
                if enable_account_money < balance_fee:
                    balance_fee = enable_account_money
            else:
                balance_fee = Decimal(0)
            accout_balance = str(account_money.ebf_am_money.quantize(Decimal("0.00")))
        else:
            balance_fee = Decimal(0)
            accout_balance = str(0.00)

        wechat_fee = price_total - balance_fee - coupon_fee
        redis = redis_util.client
        unique_str = str(uuid.uuid1())  # 页面唯一字符串，避免多次重复请求，重复支付
        redis.setex("unique_string_%s" % unique_str, 600, 10)
        if enable_account_money < 0:
            enable_account_money = Decimal("0")
        context = dict(
            store_id=store_id,
            store_address=agency.ebf_agency_address,
            store_name=agency.ebf_agency_name,
            cart_goods_array=cart_goods_array,
            message=message,
            goods_list=goods_list,
            price_total=str(price_total.quantize(Decimal("0.00"))),
            wechat_fee=str(wechat_fee.quantize(Decimal("0.00"))),
            balance_fee=str(balance_fee.quantize(Decimal("0.00"))),  # 实际可抵扣的余额
            max_balance_fee="%.2f" % max_balance_fee,  # 允许的最大支付余额
            able_use_balance_goods_fee="%.2f" % (price_total - forbid_balance_fee),  # 可用余额的商品金额
            coupon_fee=int(coupon_fee),
            old_order_id=old_order_id,
            address=address_info,
            enable_account_money=str(enable_account_money.quantize(Decimal("0.00"))),  # 可用余额
            accout_balance=accout_balance,  # 账户余额
            unique_str=unique_str,
            store_img=self.get_store_logo(store_id, agency),
            t=t
        )
        session.close()
        return self.write(ret_msg(data=context))

    async def post(self):
        open_id = await self.get_open_id()
        is_membership = self.is_membership(open_id)
        ship_type = int(self.get_parameter("ship_type", 1))  # 配送方式 0=快递物流 1=店内消费 2=门店自提 3=外卖配送
        pay_type = int(self.get_parameter("pay_type", PayType.WECHAT))
        table_id = self.get_parameter("room_id", None)
        store_id = int(self.get_parameter("store_id", 0))
        room_id = self.get_parameter("room_id", "0")
        balance_fee = Decimal(self.get_parameter("balance_fee", 0))
        coupon_fee = Decimal(self.get_parameter("coupon_fee", "0"))
        old_order_id = self.get_parameter("oid", None)
        order_type = int(self.get_parameter("order_type", 1))
        enable_balance_fee = Decimal("0")  # 订单的最大抵扣余额
        message = self.get_parameter("message", "")
        unique_str = self.get_parameter("unique_str")
        t = self.get_parameter("t", None)
        url = self.get_parameter("url", None)  # 用户请求该接口所在的页面完整地址,调用微信支付JSAPI用
        cu_id = int(self.get_parameter("cu_id", 0))
        if pay_type not in [PayType.WECHAT, PayType.BALANCE]:
            return self.write(ret_msg(code=-1, error_message="目前只支持微信支付和余额支付"))

        if not store_id:
            return self.write(ret_msg(code=-1, error_message="请求参数缺少门店编号！"))

        if cu_id and balance_fee > 0:
            return self.write(ret_msg(code=-1, error_message="优惠券和余额不能同时使用！"))

        # if not table_id and ship_type==1:
            # return self.write(ret_msg(code=-1, error_message="请输入序号或台号或房间号！"))

        ip = self.request.remote_ip
        redis = redis_util.client
        unique_string = redis.get("unique_string_%s" % unique_str)
        if not unique_string:
            return self.write(ret_msg(code=-1, error_message="页面已失效，请返回上一页面重新确认订单！"))

        class UserAddress(object):
            pass

        class GoodsOrder(object):
            pass

        class Goods(object):
            pass

        class Carts(object):
            pass

        class User(object):
            pass

        class AccountMoney(object):
            pass

        class Agency(object):
            pass

        class UserAgency(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_store_goods", "model": Goods},
            {"db_table": "sdet_store_cart", "model": Carts},
            {"db_table": "sdet_order", "model": GoodsOrder},
            {"db_table": "sdet_address", "model": UserAddress},
            {"db_table": "ebt_user", "model": User},
            {"db_table": "ebt_account_money", "model": AccountMoney},
            {"db_table": "ebt_agency", "model": Agency},
            {"db_table": "ebt_user_agency", "model": UserAgency}
        ])

        default_address = AddressHelper.get_default(open_id)
        use_address = AddressHelper.get_use(open_id)
        db = mongodb_util.db
        address = None
        user = session.query(User).filter_by(ebf_user_id=open_id).first()
        store_ua = session.query(UserAgency).filter_by(ebf_agency_id=store_id).first()
        store_user_id = store_ua.ebf_ua_bind_user_id
        store_uid = self.get_agency_uid(store_user_id)
        if not user:
            logger.info("创建用户：store_id=%s, open_id=%s" % (store_id, open_id))
            user = await self.create_user(open_id, store_uid, store_user_id)

        recommend_user = user.ebf_user_recommend_user
        flag = False
        if recommend_user:
            a_id, s_id = self.store_agency_id(recommend_user)
            if a_id and s_id and a_id != s_id and s_id == store_id:
                flag = True  # 店员分销
                clerk_uid = self.get_agency_uid(recommend_user)

        if not user.ebf_user_agency_uid or user.ebf_user_agency_uid == config.DEFAULT_AGENCY_UID:
            if flag:
                user.ebf_user_agency_uid = clerk_uid
                logger.info("更新用户上级：open_id=%s, store_id=%s, agency_uid=%s" % (open_id, store_id, clerk_uid))
            else:
                user.ebf_user_agency_uid = store_uid
                logger.info("更新用户上级：open_id=%s, store_id=%s, agency_uid=%s" % (open_id, store_id, store_uid))

        if use_address:
            logger.debug("使用地址: address_id=%s" % use_address["address_id"])
            address = session.query(UserAddress).filter_by(sdef_address_id=use_address["address_id"]).first()
        elif default_address:
            logger.debug("默认地址: address_id=%s" % default_address["address_id"])
            address = session.query(UserAddress).filter_by(sdef_address_id=default_address["address_id"]).first()
        else:
            result = db.sdec_address.find({"user_id": open_id, "is_delete": False}).sort("update_time",
                                                                                         pymongo.DESCENDING)
            for a in result:
                address = session.query(UserAddress).filter_by(sdef_address_id=a["address_id"]).first()
                if address:
                    break

        if not address and (ship_type == 0 or ship_type == 3):
            session.close()
            return self.write(ret_msg(code=-1, error_message="请先完善收货地址"))

        agency = session.query(Agency).filter_by(ebf_agency_type=3, ebf_agency_id=store_id).first()
        if not agency:
            session.close()
            return self.write(ret_msg(code=-1, error_message="门店%s不存在！" % store_id))

        cart_goods = self.get_parameter("goods_list")
        cart_goods_array = []
        price_total = Decimal("0")
        forbid_balance_fee = Decimal(0)  # 禁止使用余额的商品金额
        total_count = 0
        goods_array = []
        if t == "pickup_in_store":  # t=pickup_in_store 表示店内自提（由线上平台订单转为店内自提订单）
            cart_goods = []
            store_goods_list = self.get_parameter("store_goods_list", None)
            if not store_goods_list:
                session.close()
                return self.write(ret_msg(code=-1, error_message="缺店内自提商品列表参数！"))
            for g in store_goods_list:
                cart_goods.append({"id": g["id"], "count": g["count"], "store_id": store_id, "room_id": room_id, "is_buy": 1})
        if not cart_goods:
            session.close()
            return self.write(ret_msg(-1, error_message="请返回购物车或商城选择要购买的商品！"))

        for c in cart_goods:
            _filter = dict(
                sdef_goods_store_id=store_id,
                sdef_goods_id=c["id"],
                sdef_goods_status=1,
                sdef_goods_is_delete=0,
                sdef_goods_factory_uid=config.FACTORY_UID
            )
            _goods = session.query(Goods).filter_by(**_filter).first()

            if not _goods:
                session.close()
                return self.write(ret_msg(-1, error_message='编号为“' + c["id"] + '”的商品已下架'))

            count = int(c["count"])
            total_count += count
            goods_id = c["id"]
            goods_img = json.loads(_goods.sdef_goods_thumbnail)[0]
            goods_price = _goods.sdef_goods_member_price if is_membership else _goods.sdef_goods_price
            gname = _goods.sdef_goods_name
            goods_detail = dict(
                goodCount=count,
                goodName=gname,
                goodPrice=float("%.2f" % goods_price),
                goodUnit="支"
            )
            goods_array.append(goods_detail)
            if t == "retry":
                logger.info(f"cart_goods={cart_goods}, old_order_id={old_order_id}")
            else:
                if _goods.sdef_goods_count < 1:
                    session.close()
                    return self.write(ret_msg(-1, error_message=_goods.sdef_goods_name + "库存不足"))

                # 检查商品是否限购
                if _goods.sdef_goods_limit_num != 0:
                    if _goods.sdef_goods_limit_num <= count:
                        session.close()
                        return self.write(ret_msg(-1, error_message=gname + "限购"))

                # 下单时减少库存
                if _goods.sdef_goods_count >= count:
                    old_goods_count = _goods.sdef_goods_count
                    logger.info(f"payconfirm: old_goods_count={old_goods_count} minus=-{count} store_id={store_id}")
                    _goods.sdef_goods_count -= count
                else:
                    session.close()
                    return self.write(ret_msg(-1, error_message=gname + "库存不足"))

            goods = dict(
                id=goods_id,
                count=count,
                goods_img=goods_img,
                goods_name=_goods.sdef_goods_name,
                goods_price=goods_price,
                goods_member_price=_goods.sdef_goods_member_price,
                goods_pin_price=_goods.sdef_goods_pin_price,
                balance_rate=_goods.sdef_goods_balance_rate,
                rate=_goods.sdef_goods_rate,
                master_rate=_goods.sdef_goods_master_rate,
                invite_rate=_goods.sdef_goods_invite_rate,
                invite_invite_rate=_goods.sdef_goods_invite_invite_rate,
                career_rate=_goods.sdef_goods_career_rate,
                invite_career_rate=_goods.sdef_goods_invite_career_rate,
                supplier=_goods.sdef_goods_supplier,
                goods_cost=_goods.sdef_goods_cost,
                store_id=store_id,
                room_id=table_id
            )
            enable_balance_fee += Decimal(_goods.sdef_goods_balance_rate) * Decimal(count) * goods_price
            price_total += goods_price * Decimal(count)
            cart_goods_array.append(goods)
            if _goods.sdef_goods_balance_rate == 0:
                forbid_balance_fee += goods_price * Decimal(count)

        # 获取账户余额
        account_money = session.query(AccountMoney).filter_by(ebf_am_user_id=open_id).first()
        lock_balance_fee = redis.get("balance_lock_%s" % open_id)
        if account_money:
            # 钱包余额需要减去redis缓存中暂扣的金额
            enable_account_money = account_money.ebf_am_money
            if lock_balance_fee:
                enable_account_money = Decimal(enable_account_money) - Decimal(lock_balance_fee.decode())

            if account_money.ebf_am_is_forbid == 0 and enable_account_money > 0:
                if enable_account_money < enable_balance_fee:
                    enable_balance_fee = enable_account_money
            else:
                enable_balance_fee = Decimal("0")
        else:
            enable_balance_fee = Decimal("0")

        if enable_balance_fee < balance_fee:
            session.close()
            return self.write(ret_msg(code=-1, error_message="可抵扣余额错误"))

        able_use_balance_goods_fee = price_total - forbid_balance_fee  # 可使用余额的商品金额
        if cu_id:
            is_use_coupon = get_available_coupons(store_id, order_type, open_id,
                                                  able_use_balance_goods_fee, cart_goods, 10, 0, cu_id=cu_id)
            if not is_use_coupon:
                session.close()
                return self.write(ret_msg(code=-1, error_message="优惠券ID错误"))

            real_coupon_fee = is_use_coupon[0]["coupon_money"]
            if coupon_fee != real_coupon_fee:
                session.close()
                return self.write(ret_msg(code=-1, error_message="优惠券金额错误"))

        wechat_fee = price_total - balance_fee - coupon_fee
        logger.info("price_total=%s,balance_fee=%s,wechat_fee=%s" % (price_total, balance_fee, wechat_fee))
        # 计算订单序号,统计所有门店
        today = datetime.date.today()
        year = today.year
        month = today.month
        day = today.day
        poi_seq = session.query(func.count(GoodsOrder.sdef_order_id)).filter(
            GoodsOrder.sdef_order_store_id > 0).filter(and_(
                extract('year', GoodsOrder.sdef_order_create_time) == year,
                extract('month', GoodsOrder.sdef_order_create_time) == month,
                extract('day', GoodsOrder.sdef_order_create_time) == day)).scalar() + 1
        table_id = "%04d" % poi_seq

        if coupon_fee > Decimal("0"):
            if wechat_fee <= Decimal("0") and balance_fee == Decimal("0"):
                pay_type = PayType.COUPON  # 支付方式标记为优惠券抵扣
        elif wechat_fee < Decimal("0.01"):
            pay_type = PayType.BALANCE  # 支付方式直接标记为余额支付
        else:
            pass

        now = datetime.datetime.now()
        goods_order = GoodsOrder()
        out_trade_no = generator_trade_no()
        goods_order.sdef_order_id = out_trade_no
        goods_order.sdef_order_factory_uid = config.FACTORY_UID
        goods_order.sdef_order_device_id = open_id
        goods_order.sdef_order_fee = wechat_fee
        goods_order.sdef_order_goods = json.dumps(cart_goods_array, cls=JSONEncoder)
        goods_order.sdef_order_balance_fee = balance_fee
        goods_order.sdef_order_name = "小酒馆订单"
        goods_order.sdef_order_message = message
        goods_order.sdef_order_store_id = store_id
        goods_order.sdef_order_room_id = table_id
        goods_order.sdef_order_ship_type = ship_type
        goods_order.sdef_order_coupon_fee = coupon_fee
        if ship_type == 3:
            goods_order.sdef_order_address_id = address.sdef_address_id

        if pay_type == PayType.WECHAT:
            goods_order.sdef_order_status = OrderStatus.WAIT_PAY
            goods_order.sdef_order_pay_status = PayStatus.NOT_PAY
        else:
            goods_order.sdef_order_status = OrderStatus.WAIT_EXPRESS
            goods_order.sdef_order_pay_status = PayStatus.SUCCESS
            goods_order.sdef_order_pay_time = now

        goods_order.sdef_order_pay_type = pay_type
        goods_order.sdef_order_type = order_type
        goods_order.sdef_order_ip = ip
        goods_order.sdef_order_delivery_fee = 0  # 邮费
        goods_order.sdef_order_create_time = now
        if flag:
            goods_order.sdef_order_agency_uid = clerk_uid
            goods_order.sdef_order_describe = recommend_user
        else:
            goods_order.sdef_order_agency_uid = store_uid

        if not (ship_type == 3 and pay_type in [4, 5]):
            session.add(goods_order)
        logger.info("order_id=%s, oid=%s, store_id=%s, rec_user=%s" %
                    (out_trade_no, open_id, store_id, recommend_user))
        redis = redis_util.client
        redis.setex("order_id_%s" % out_trade_no, 86400, out_trade_no)
        if coupon_fee > Decimal("0"):
            self.create_coupon_order(open_id, cu_id, out_trade_no, store_id, price_total,
                                     coupon_fee, wechat_fee, balance_fee)

        if t == "buy":
            pass
        elif t == "retry":
            old_order_id = self.get_parameter("oid", None)
            old_order = session.query(GoodsOrder).filter_by(sdef_order_device_id=open_id,
                                                            sdef_order_id=old_order_id,
                                                            sdef_order_is_delete=0).first()

            if old_order.sdef_order_status != OrderStatus.WAIT_PAY:
                session.close()
                return self.write(ret_msg(code=-1, error_message="只有处于待支付的订单才能继续支付"))

            # 获取之前的支付信息，创建完新订单之后将之前的订单标记为删除
            old_order.sdef_order_is_delete = 1
            old_order.sdef_order_delete_time = datetime.datetime.now()
            old_order.sdef_order_status = OrderStatus.INVALID
            logger.info("将旧订单标记为删除：%s" % old_order_id)
        elif t == "buy_again":
            pass
        elif t == "pickup_in_store":
            pass
        else:
            cart = session.query(Carts).filter_by(sdef_cart_device_id=open_id, sdef_cart_store_id=store_id).first()
            if not cart:
                session.close()
                return self.write(ret_msg(-1, error_message="请求参数错误！"))

            new_cart_goods = []
            old_cart_goods = json.loads(cart.sdef_cart_goods)
            if len(old_cart_goods) == len(cart_goods):
                ret = session.query(Carts).filter_by(sdef_cart_device_id=open_id, sdef_cart_store_id=store_id).delete()
            else:
                ret = 0
                for c in old_cart_goods:
                    if not c.get("is_buy", None):
                        new_cart_goods.append(c)

                session.query(Carts).filter_by(
                    sdef_cart_device_id=open_id,
                    sdef_cart_store_id=store_id
                ).update({Carts.sdef_cart_goods: json.dumps(new_cart_goods)})
            logger.info("删除购物车: delete cart ret=%s" % ret)

        if ship_type == 3:
            o_id = out_trade_no
            name = address.sdef_address_name
            phone = address.sdef_address_phone
            shop_id = str(store_id)
            weight = float("%.2f" % (0.75 * total_count))
            fee = float("%.2f" % price_total)
            g_detail = dict(goods=goods_array)
            g_detail = json.dumps(g_detail)
            province = address.sdef_address_province
            city = address.sdef_address_city
            county = address.sdef_address_county
            address_detail = address.sdef_address_detail
            addr = province + city + county + address_detail
            addr = addr.replace(' ', '')
            try:
                lng, lat = ApiHelper.geocode(addr)
                lng = float(lng)
                lat = float(lat)
            except Exception as e:
                session.close()
                logger.error(f"获取经纬度失败: err={e}")
                return self.write(ret_msg(code=-1, error_message="获取经纬度失败"))
            if pay_type not in [4, 5]:
                content = f"{name}_{phone}_{addr}_{weight}_{fee}_{g_detail}_{lng}_{lat}"
                redis.setex("order_id_info_%s" % o_id, 3600, content)

        redis.delete("unique_string_%s" % unique_str)
        if pay_type == PayType.BALANCE:
            logger.info("余额支付")
            account_money = session.query(AccountMoney).filter_by(ebf_am_user_id=open_id).first()
            if not account_money:
                session.close()
                return self.write(ret_msg(-1, error_message="账户未开通"))

            if account_money.ebf_am_is_forbid != 0:
                session.close()
                return self.write(ret_msg(-1, error_message="账户不可用"))

            if account_money.ebf_am_money < price_total:
                session.close()
                return self.write(ret_msg(-1, error_message="账户余额不足"))

            price_total_rate = calculate_order_fee(goods_order.sdef_order_goods)[0]
            # 扣款并添加扣款记录，同时往ebt_pay表中添加一条订单
            if not BalanceHandler.reduce_money(open_id, out_trade_no, price_total, price_total_rate, ip):
                session.close()
                return self.write(ret_msg(-1, error_message="扣款失败"))

            result = create_balance_record(open_id, price_total, out_trade_no)
            if result:
                logger.info("创建余额消费记录成功:oid=%s,out_trade_no=%s" % (open_id, out_trade_no))
            else:
                logger.error("创建余额消费记录失败:oid=%s,out_trade_no=%s" % (open_id, out_trade_no))

            logger.info(f"触发余额支付成功异步任务 order_id={out_trade_no}")
            # 8小时之后自动确认收货
            if not add_mq_task(out_trade_no, message_type=6, delay=8 * 3600):
                logger.info(f"8小时后自动确认收货加入任务队列失败：out_trade_no={out_trade_no}")
            tasks.order_paid.delay(out_trade_no)
            tasks.cloud_print_order.delay(out_trade_no)
            # 计算订单预估收益 - 异步调用
            # await calculate_pre_benefit(out_trade_no)
            # 创建美团内部订单
            if ship_type == 3:
                res = await self.create_meituan_order(o_id, shop_id, name, addr, phone,
                                                      lng, lat, weight, g_detail, fee, table_id)
                if res and res["code"] == 0:
                    mt_peisong_id = res["data"]["mt_peisong_id"]
                    goods_order.sdef_order_mt_peisong_id = mt_peisong_id
                else:
                    msg = None
                    if res:
                        msg = res["message"]
                    logger.error(f"创建美团内部订单失败:order_id={o_id}, err_msg={msg}")
                session.add(goods_order)
            session.commit()
            session.close()
            context = dict(
                trade_no=out_trade_no,
                pay_type=pay_type
            )
            return self.write(ret_msg(data=context))

        if pay_type == PayType.COUPON:
            logger.info(f"优惠券抵扣: order_id={out_trade_no}, cu_id={cu_id}, oid={open_id}")
            self.update_coupons_status(cu_id, open_id, out_trade_no)
            logger.info(f"触发纯优惠券支付成功异步任务 order_id={out_trade_no}")
            # 8小时之后自动确认收货
            if not add_mq_task(out_trade_no, message_type=6, delay=8 * 3600):
                logger.info(f"8小时后自动确认收货加入任务队列失败：out_trade_no={out_trade_no}")
            tasks.order_paid.delay(out_trade_no)
            tasks.cloud_print_order.delay(out_trade_no)
            # 创建美团内部订单
            if ship_type == 3:
                res = await self.create_meituan_order(o_id, shop_id, name, addr, phone,
                                                      lng, lat, weight, g_detail, fee, table_id)
                if res and res["code"] == 0:
                    mt_peisong_id = res["data"]["mt_peisong_id"]
                    goods_order.sdef_order_mt_peisong_id = mt_peisong_id
                else:
                    msg = None
                    if res:
                        msg = res["message"]
                    logger.error(f"创建美团内部订单失败:order_id={o_id}, err_msg={msg}")
                session.add(goods_order)
            session.commit()
            session.close()
            context = dict(pay_type=5)
            return self.write(ret_msg(data=context))

        try:
            session.commit()
        except Exception as e:
            logger.exception(e)
            session.rollback()
            session.close()
            return self.write(ret_msg(code=-1, error_message="订单创建异常"))

        app_id = config.MP_APP_ID
        time_stamp = int(str(time.time()).split(".")[0])
        nonce_str = WechatPay.generate_nonce_str()
        logger.info("生成预支付交易单: order_id=%s, oid=%s" % (out_trade_no, open_id))

        try:
            prepay_id = WechatPay.store_order(out_trade_no, int(wechat_fee * 100), "线下门店订单", ip, nonce_str, open_id)

            # 将暂扣的余额存入redis中，超时时间10分钟，微信支付回调时直接扣余额即可
            if lock_balance_fee:
                lock_balance_fee = Decimal(lock_balance_fee.decode()) + balance_fee
            else:
                lock_balance_fee = balance_fee
            redis.setex("balance_lock_%s" % open_id, 600, str(lock_balance_fee))

            if use_address:
                if default_address:
                    if use_address["address_id"] != default_address["address_id"]:
                        is_default = True
                        is_use = True
                        AddressHelper.set_default(open_id, default_address["address_id"], is_default, is_use)
                else:
                    is_default = False
                    is_use = False
                    AddressHelper.set_default(open_id, use_address["address_id"], is_default, is_use)
        except Exception as e:
            logger.exception(e)
            session.close()
            return self.finish(ret_msg(code=-1, error_message="支付异常"))

        package = "prepay_id=" + prepay_id
        # 计算jsapi签名
        logger.info("计算jsapi签名" + package)
        sign_value = WechatPay.get_jsapi_pay_sign(time_stamp, nonce_str, package, "MD5")
        jsapi_ticket = self.get_jsapi_ticket()
        signature = Wechat_Base.signature(nonce_str, time_stamp, url, jsapi_ticket)
        logger.debug("signature：%s, url: %s" % (signature, url))
        # 微信支付的加入消息队列，6分钟后检查是否已支付
        jid = add_mq_task(out_trade_no, message_type=1, delay=360)
        logger.info(f"order_id={out_trade_no}, jid={jid}")

        context = dict(
            wechat_fee="%.2f" % wechat_fee if wechat_fee else "0.00",
            trade_no=out_trade_no,
            app_id=app_id,
            time_stamp=str(time_stamp),
            nonce_str=nonce_str,
            sign_value=sign_value,
            prepay_id=prepay_id,
            signature=signature,
            full_url=url,
            order_type=order_type,
            pay_type=pay_type
        )

        session.close()
        return self.write(ret_msg(data=context))


class WechatNotify(ApiBaseHandler):
    async def post(self):
        logger.info("wechat_async_notify")
        pay_info = self.request.body.decode("utf8").replace('\n', '').replace(' ', '')
        if not pay_info:
            return self.write(wechat_resp(40001))

        params = xmltodict.parse(pay_info, encoding='utf-8').get('xml', None)
        logger.info("xml解析成功, params=%s" % params)
        # 参数传入错误
        if not params:
            ret = wechat_resp(40001)
            logger.info(ret)
            return self.write(ret)

        # 验证传输码
        return_code = params.get('return_code', None)
        if not return_code or return_code != 'SUCCESS':
            ret = wechat_resp(46011)
            logger.info(ret)
            return self.write(ret)

        # 验证交易码
        result_code = params.get('result_code', None)
        if not result_code or result_code != 'SUCCESS':
            ret = wechat_resp(46012)
            logger.info(ret)
            return self.write(ret)

        # 验证微信消息
        if not weixin_verify(params):
            ret = wechat_resp(46005)
            logger.info(ret)
            return self.write(ret)

        # 验证订单号
        out_trade_no = params.get('out_trade_no', None)
        if not out_trade_no:
            ret = wechat_resp(46004)
            logger.info(ret)
            return self.write(ret)

        class GoodsOrder(object):
            pass

        class User(object):
            pass

        class Pay(object):
            pass

        class Agency(object):
            pass

        class UserAgency(object):
            pass

        class Benefit(object):
            pass

        class Printer(object):
            pass

        class CouponUser(object):
            pass

        class CouponOrder(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_order", "model": GoodsOrder},
            {"db_table": "ebt_user", "model": User},
            {"db_table": "ebt_pay", "model": Pay},
            {"db_table": "ebt_agency", "model": Agency},
            {"db_table": "ebt_user_agency", "model": UserAgency},
            {"db_table": "sdet_benefit", "model": Benefit},
            {"db_table": "sdet_store_printer", "model": Printer},
            {"db_table": "ebt_coupon_user", "model": CouponUser},
            {"db_table": "ebt_coupon_order", "model": CouponOrder},
        ])

        _order_array = session.query(GoodsOrder).filter_by(sdef_order_id=out_trade_no).all()
        if len(_order_array) == 0:
            # 订单不存在异常（订单不存在的时候，厂商和合伙人都为空）
            ret = wechat_resp(46002)
            logger.info(ret)
            session.close()
            return self.write(ret)
        if len(_order_array) > 1:
            # 订单多条记录异常
            ret = wechat_resp(46003)
            logger.info(ret)
            session.close()
            return self.write(ret)

        pay = _order_array[0]

        # 如果订单记录的支付状态不是0（未完成）， 则直接返回即可，不需要继续处理
        if pay.sdef_order_pay_status != PayStatus.NOT_PAY:
            session.close()
            return self.write(wechat_resp(0))

        # 验证支付费用是否一致
        pay_fee = pay.sdef_order_fee
        total_fee = params.get('total_fee', None)
        logging.debug("pay_fee:%s,total_fee:%s" % (pay_fee, total_fee))
        if (not pay_fee) or (not total_fee) or (("%.2f" % (float(pay_fee) * 100)) != ("%.2f" % float(total_fee))):
            ret = wechat_resp(46001)
            logger.info(ret)
            session.close()
            return self.write(ret)

        # 更新订单为已支付
        logger.info("out_trade_no=%s" % out_trade_no)
        user_id = pay.sdef_order_device_id
        pay.sdef_order_status = OrderStatus.WAIT_EXPRESS
        pay.sdef_order_pay_status = PayStatus.SUCCESS
        pay.sdef_order_pay_time = datetime.datetime.now()
        if pay.sdef_order_ship_type == 3:
            shop_id = pay.sdef_order_store_id
            poi_seq = pay.sdef_order_room_id
            mt_peisong_id = await self.create_meituan_order(out_trade_no, shop_id, poi_seq)
            if mt_peisong_id:
                pay.sdef_order_mt_peisong_id = mt_peisong_id
                logger.info(f"创建美团内部订单成功: order_id={out_trade_no}")
        if pay.sdef_order_coupon_fee > 0:
            co = session.query(CouponOrder).filter_by(order_id=out_trade_no).first()
            cu_id = co.coupon_user_id
            cu = session.query(CouponUser).filter_by(id=cu_id).one()
            co.status = 1
            cu.status = 1
        session.commit()

        # 扣除锁定的余额
        if pay.sdef_order_balance_fee > Decimal("0"):
            is_success = update_account_money(user_id, -pay.sdef_order_balance_fee)
            if not is_success:
                logger.error("扣款异常:user_id=%s,out_trade_no=%s" % (user_id, out_trade_no))
            else:
                result = create_balance_record(user_id, pay.sdef_order_balance_fee, out_trade_no)
                if result:
                    logger.info("创建余额消费记录成功:user_id=%s,out_trade_no=%s" % (user_id, out_trade_no))
                else:
                    logger.error("创建余额消费记录失败:user_id=%s,out_trade_no=%s" % (user_id, out_trade_no))
            # 付款后解除锁定余额
            redis = redis_util.client
            lock_balance_fee = redis.get("balance_lock_%s" % user_id)
            if lock_balance_fee:
                lock_balance_fee = Decimal(lock_balance_fee.decode())
                lock_balance_fee -= pay.sdef_order_balance_fee
                if lock_balance_fee > Decimal("0"):
                    redis.setex("balance_lock_%s" % user_id, 600, str(lock_balance_fee))
                else:
                    redis.delete("balance_lock_%s" % user_id)

        # 打印订单
        total_fee = pay_fee + pay.sdef_order_balance_fee + pay.sdef_order_coupon_fee
        total_fee = "%.2f" % total_fee
        content = self.get_content(pay, total_fee)
        store_id = pay.sdef_order_store_id
        filters = dict(
            ebf_store_id=store_id,
            ebf_printer_status=1
        )
        printer = session.query(Printer).filter_by(**filters).first()
        if printer:
            sn = printer.ebf_printer_serial_number
            times = str(printer.ebf_printer_copies)
            logger.info("sn=%s, order_id=%s, store_id=%s" % (sn, out_trade_no, store_id))
            # TODO tasks.print_order.delay(out_trade_no)
            tasks.print_order.delay(sn, content, out_trade_no, store_id, times)

        # 8小时之后自动确认收货
        if not add_mq_task(out_trade_no, message_type=6, delay=8 * 3600):
            logger.info(f"8小时后自动确认收货加入任务队列失败：out_trade_no={out_trade_no}")

        # 触发支付成功异步任务
        tasks.order_paid.delay(out_trade_no)
        logger.info(f"支付回调处理成功, order_id={out_trade_no}")

        # # 给店家及所有店员发送新订单提醒
        # oid_list = await self.get_clerk_oid(store_id)
        # room_id = pay.sdef_order_room_id
        # tasks.send_new_order_notice0.delay(oid_list, room_id, store_id, out_trade_no)

        # 创建优惠券记录
        # pay_money = pay_fee + pay.sdef_order_balance_fee
        # await self.create_coupon(out_trade_no, user_id, store_id, pay_money, pay.sdef_order_goods)

        # 发送订单信息到mqtt服务器
        msg = self.get_msg(pay, total_fee, out_trade_no, store_id)
        tasks.send_order_to_mqtt.delay(msg, out_trade_no, store_id)

        # 计算预估收益 - 异步调用
        # await calculate_pre_benefit(out_trade_no)

        session.close()
        return self.write(wechat_resp(0))

    async def create_meituan_order(self, order_id, shop_id, poi_seq):
        """创建美团内部订单"""
        redis = redis_util.client
        order_info = redis.get("order_id_info_%s" % order_id)
        if order_info:
            shop_id = str(shop_id)
            order_info = order_info.decode()
            order_list = order_info.split("_")
            name = order_list[0]
            phone = order_list[1]
            addr = order_list[2]
            weight = float(order_list[3])
            fee = float(order_list[4])
            g_detail = order_list[5]
            lng = float(order_list[6])
            lat = float(order_list[7])
            o = CreateOrderByShop(order_id, shop_id, name, addr, phone, lng, lat, poi_seq, weight, g_detail, fee)
            result = o.create_shop_order()
            logger.info(f"order_id={order_id}, order_info={order_info}, result={result}")
            if result and result["code"] == 0:
                mt_peisong_id = result["data"]["mt_peisong_id"]
                return mt_peisong_id
            else:
                msg = None
                if result:
                    msg = result["message"]
                logger.error(f"创建美团内部订单失败: order_id={order_id}, err_msg={msg}")
                return False
        else:
            logger.error(f"创建美团内部订单失败：redis缓存数据为空, order_id={order_id}")
            return False

    async def get_clerk_oid(self, store_id):
        class User(object):
            pass

        class Agency(object):
            pass

        class UserAgency(object):
            pass

        session = self.get_db_session([
            {"db_table": "ebt_user", "model": User},
            {"db_table": "ebt_agency", "model": Agency},
            {"db_table": "ebt_user_agency", "model": UserAgency},
        ])

        oid_list = list()
        agencys = session.query(Agency).filter_by(
            ebf_agency_parent_id=store_id,
            ebf_agency_type=4).order_by(Agency.ebf_agency_id.desc()).all()
        for g in agencys:
            id = g.ebf_agency_id
            ua = session.query(UserAgency).filter_by(ebf_agency_id=id).first()
            if ua and ua.ebf_ua_bind_user_id:
                oid_list.append(ua.ebf_ua_bind_user_id)

        ua2 = session.query(UserAgency).filter_by(ebf_agency_id=store_id).first()
        if ua2 and ua2.ebf_ua_bind_user_id:
            oid_list.append(ua2.ebf_ua_bind_user_id)
        session.close()
        return oid_list

    def get_content(self, pay, total_fee):
        cart_goods = json.loads(pay.sdef_order_goods)
        room_id = pay.sdef_order_room_id
        create_time = pay.sdef_order_create_time.strftime("%Y-%m-%d %H:%M:%S")
        id = pay.sdef_order_id
        table_name = "序号"
        if pay.sdef_order_ship_type == 3:
            table_name = "美团"
        content = "<CB>莱支好酒</CB><BR>"
        content += "................................<BR>"
        if len(bytes(f'-- {table_name}:{room_id} --', encoding='utf8')) > 20:
            content += "<C><L>-- {}: {} --</L></C><BR>".format(table_name, room_id)
        else:
            content += "<CB>-- {}:{} --</CB><BR>" . format(table_name, room_id)
        content += "下单时间：{}<BR>".format(create_time)
        content += "订单编号：{}<BR>".format(id)
        content += "※※※※※※  商品  ※※※※※※<BR>"
        content += "名称　　     单价   数量    金额<BR>"
        content += "--------------------------------<BR>"
        for g in cart_goods:
            name = g["goods_name"].replace(" ", "")
            price = g["goods_price"]
            count = g["count"]
            fee = "%.2f" % Decimal(price * count)
            if len(name) > 7:
                content += "{}<BR>".format(name)
                content += "<RIGHT>     　   {}   ×{}   {}</RIGHT><BR>".format(price, count, fee)
            elif len(str(fee)) > 5:
                content += "<RIGHT>{}　{}  ×{}  {}</RIGHT><BR>" . format(name, price, count, fee)
            else:
                content += "<RIGHT>{}　 {}  ×{}   {}</RIGHT><BR>".format(name, price, count, fee)

        content += "<BR>"
        content += "备注： <BR>"
        content += "--------------------------------<BR>"
        content += "合计：{}元<BR><BR>".format(total_fee)
        content += "※※※※※※※※※※※※※※※※<BR>"
        content += "<CB>* 欢迎下次光临 *</CB><BR>"
        return content

    def get_msg(self, pay, total_fee, order_id, store_id):
        msg = dict(
            user_id=pay.sdef_order_device_id,
            user_nickname=None,
            waiter_id=pay.sdef_order_describe,
            waiter_nickname=None,
            store_id=store_id,
            store_name=None,
            total_fee=total_fee,
            order_id=order_id,
            order_type=pay.sdef_order_type,
            pay_type=pay.sdef_order_pay_type,
            pay_status=pay.sdef_order_pay_status,
            order_status=pay.sdef_order_status,
            create_time=pay.sdef_order_create_time.strftime("%Y-%m-%d %H:%M:%S"),
            pay_time=pay.sdef_order_pay_time.strftime("%Y-%m-%d %H:%M:%S") if pay.sdef_order_pay_time else None,
            ship_type=pay.sdef_order_ship_type,
            products=json.loads(pay.sdef_order_goods)
        )
        return msg

    def is_allow(self, order_id, open_id, pay_money, cart_goods):
        class CouponUser(object):
            pass

        session = self.get_db_session([
            {"db_table": "ebt_coupon_user", "model": CouponUser}
        ])

        goods_id = "523948088470142"
        filters = dict(
            user_id=open_id,
            coupon_id=1,
            order_id=order_id
        )
        cu = session.query(CouponUser).filter_by(**filters).first()
        if (pay_money) < Decimal("139"):
            session.close()
            return False

        if cu:
            session.close()
            return False

        cart_goods = json.loads(cart_goods)
        result = list(filter(lambda g: g["id"] == goods_id, cart_goods))
        if not result:
            session.close()
            return False

        session.close()
        return True

    async def create_coupon(self, order_id, open_id, store_id, pay_money, cart_goods):
        result = self.is_allow(order_id, open_id, pay_money, cart_goods)
        # 0=表示取消线下门店139无门槛抵扣券
        count = 0  # self.is_first_buy(open_id)
        if not result:
            return False

        class CouponUser(object):
            pass

        session = self.get_db_session([
            {"db_table": "ebt_coupon_user", "model": CouponUser}
        ])

        filters = dict(
            user_id=open_id,
            coupon_id=3,
            order_id=order_id
        )
        cu1 = session.query(CouponUser).filter_by(**filters).first()
        try:
            now = datetime.datetime.now()
            cu = CouponUser()
            cu.user_id = open_id
            cu.coupon_id = 1
            cu.store_id = store_id
            cu.order_id = order_id
            cu.coupon_money = 139
            cu.create_time = now
            cu.full_money = 500
            cu.is_active = 0
            cu.status = 0
            session.add(cu)

            if count == 1 and not cu1:
                cu2 = CouponUser()
                cu2.user_id = open_id
                cu2.coupon_id = 3
                cu2.store_id = store_id
                cu2.order_id = order_id
                cu2.coupon_money = 139
                cu2.create_time = now
                cu2.is_active = 0
                cu2.status = 0
                session.add(cu2)
            session.commit()
            logger.info(f"create_time={now}, order_id={order_id}, count={count}")
        except Exception as e:
            logger.error(e)
            session.rollback()
            session.close()
            return False

        session.close()
        return True


class UserAccount(ApiBaseHandler):
    async def get(self):
        class User(object):
            pass

        session = self.get_db_session([
            {"db_table": "ebt_user", "model": User}
        ])

        open_id = await self.get_open_id()
        user = session.query(User).filter_by(ebf_user_id=open_id).first()
        if not user:
            session.close()
            return self.write(ret_msg(code=-1, error_message="账号不存在！"))

        context = dict(
            nickname=user.ebf_user_nickname,
            headurl=user.ebf_user_headurl
        )
        session.close()
        return self.write(ret_msg(data=context))


class Order(ApiBaseHandler):
    async def get(self):
        open_id = await self.get_open_id()
        order_status = int(self.get_parameter("t", 0))
        page_index = int(self.get_parameter("page_index", 0))
        page_size = int(self.get_parameter("page_size", 10))
        ApiHelper.inspect_common_params(page_index, page_size)
        page_start = page_index * page_size

        class GoodsOrder(object):
            pass

        class Goods(object):
            pass

        class Agency(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_order", "model": GoodsOrder},
            {"db_table": "sdet_store_goods", "model": Goods},
            {"db_table": "ebt_agency", "model": Agency}
        ])
        filters = dict(
            sdef_order_is_delete=0,
            sdef_order_device_id=open_id
        )

        order_array = []
        if order_status:
            filters["sdef_order_status"] = order_status

        order_list = session.query(GoodsOrder).filter_by(**filters).filter(
            GoodsOrder.sdef_order_type != OrderType.MEMBER).filter(
            GoodsOrder.sdef_order_store_id > 0).order_by(
            GoodsOrder.sdef_order_id.desc()
        )[page_start:page_start + page_size]

        for o in order_list:
            status_code = o.sdef_order_status
            if o.sdef_order_pay_status == PayStatus.REFUND:
                status_code = OrderStatus.REFUND
                o.sdef_order_status = OrderStatus.REFUND

            store_id = o.sdef_order_store_id
            price_total = Decimal(0)
            cart_goods = json.loads(o.sdef_order_goods)
            agency = session.query(Agency).filter_by(ebf_agency_id=store_id).first()
            flag = 1  # 1=表示门店还在， 0=表示门店已撤销
            if agency.ebf_agency_type != 3:
                flag = 0

            for c in cart_goods:
                price_total += Decimal(c["goods_price"]) * Decimal(c["count"])

            if not agency:
                logger.error("门店不存在： order_id=%s, store_id=%s" % (o.sdef_order_id, store_id))

            if o.sdef_order_type == OrderType.PINGUI:
                service_fee = ApiHelper.service_fee(price_total)
            else:
                service_fee = Decimal(0)
            delivery_fee = o.sdef_order_delivery_fee
            delivery_fee = Decimal(delivery_fee) if delivery_fee else Decimal(0)
            price_total += service_fee
            price_total += delivery_fee

            order_array.append(dict(
                order_id=o.sdef_order_id,
                mt_peisong_id=o.sdef_order_mt_peisong_id,
                ship_type=o.sdef_order_ship_type,
                store_name=agency.ebf_agency_name if agency else None,
                store_id=store_id,
                status_code=status_code,
                cart_goods=cart_goods,
                goods_count=len(cart_goods),
                price_total="%.2f" % price_total,
                order_fee="%.2f" % o.sdef_order_fee,
                balance_fee="%.2f" % o.sdef_order_balance_fee,
                coupon_fee="%.2f" % o.sdef_order_coupon_fee,
                service_fee="%.2f" % service_fee,
                delivery_fee="%.2f" % delivery_fee,
                pay_type=o.sdef_order_pay_type,
                create_time=o.sdef_order_create_time.strftime("%Y-%m-%d %H:%M:%S"),
                room_id=o.sdef_order_room_id,
                flag=flag,
                order_type=o.sdef_order_type
            ))

        context = dict(
            order_array=order_array,
            order_status=order_status
        )
        session.commit()
        session.close()
        return self.finish(ret_msg(data=context))

    async def post(self):
        open_id = await self.get_open_id()
        action = self.get_parameter("action", None)
        order_id = self.get_parameter("order_id", None)
        mt_peisong_id = self.get_parameter("mt_peisong_id", None)

        if not all((action, order_id)):
            return self.write(ret_msg(code=-1, error_message="缺少参数"))

        class GoodsOrder(object):
            pass

        class Goods(object):
            pass

        class CouponOrder(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_order", "model": GoodsOrder},
            {"db_table": "sdet_store_goods", "model": Goods},
            {"db_table": "ebt_coupon_order", "model": CouponOrder},
        ])

        _filter = dict(
            sdef_order_id=order_id,
            sdef_order_device_id=open_id
        )
        _order = session.query(GoodsOrder).filter_by(**_filter).first()
        if not _order:
            session.close()
            return self.write(ret_msg(code=-1, error_message="订单编号错误!"))

        if action == "cancel":
            ship_type = _order.sdef_order_ship_type
            status_enum = [OrderStatus.WAIT_PAY, OrderStatus.WAIT_EXPRESS,
                           OrderStatus.EXPRESSED, OrderStatus.RECEIVED]
            if ship_type == 1:
                if _order.sdef_order_status not in status_enum:
                    session.close()
                    return self.write(ret_msg(code=-1, error_message="只有待支付、待接单、配送中、已收货的订单才能取消"))
            elif ship_type == 3:
                if _order.sdef_order_status not in status_enum[:-1]:
                    session.close()
                    return self.write(ret_msg(code=-1, error_message="只有待支付、待接单、配送中的订单才能取消"))
            else:
                if _order.sdef_order_status not in [OrderStatus.WAIT_PAY, OrderStatus.WAIT_EXPRESS]:
                    session.close()
                    return self.write(ret_msg(code=-1, error_message="只有待支付、待发货的订单才能取消"))

            if _order.sdef_order_type == OrderType.NEW_GIFT:
                session.close()
                return self.write(ret_msg(code=-1, error_message="礼品订单不能取消"))

            if _order.sdef_order_status == OrderStatus.WAIT_PAY:
                _order.sdef_order_status = OrderStatus.INVALID
                _order.sdef_order_is_delete = 1
                _order.sdef_order_delete_time = datetime.datetime.now()
                order_goods = json.loads(_order.sdef_order_goods)
                store_id = _order.sdef_order_store_id
                for g in order_goods:
                    filter = dict(
                        sdef_goods_id=g["id"],
                        sdef_goods_status=1,
                        sdef_goods_is_delete=0,
                        sdef_goods_store_id=store_id,
                        sdef_goods_factory_uid=config.FACTORY_UID
                    )
                    goods = session.query(Goods).filter_by(**filter).first()
                    if goods:
                        goods.sdef_goods_count += int(g["count"])

                if _order.sdef_order_coupon_fee > Decimal("0"):
                    filters = dict(
                        user_id=open_id,
                        order_id=order_id,
                        status=0
                    )
                    ret = session.query(CouponOrder).filter_by(**filters).delete()
                    logger.info("delete coupon_order ret=%s, order_id=%s" % (ret, order_id))
                session.commit()
                # 取消订单时解锁余额
                if _order.sdef_order_balance_fee > Decimal("0"):
                    redis = redis_util.client
                    lock_balance_fee = redis.get("balance_lock_%s" % open_id)
                    if lock_balance_fee:
                        lock_balance_fee = Decimal(lock_balance_fee.decode())
                        lock_balance_fee -= _order.sdef_order_balance_fee
                        if lock_balance_fee > Decimal("0"):
                            redis.setex("balance_lock_%s" % open_id, 600, str(lock_balance_fee))
                        else:
                            redis.delete("balance_lock_%s" % open_id)
            elif _order.sdef_order_status in status_enum[1:]:
                goods_id1 = "558922117868242"  # 私人酒庄商品1
                goods_id2 = "561774248859721"  # 私人酒庄商品2
                if goods_id1 in _order.sdef_order_goods or goods_id2 in _order.sdef_order_goods:
                    session.close()
                    return self.write(ret_msg(code=-1, error_message="含有私人酒庄商品的订单不能退款"))

                if _order.sdef_order_pay_type == PayType.COUPON:
                    session.close()
                    return self.finish(ret_msg(code=-1, error_message="实付款为0的纯优惠券抵扣订单不能取消"))

                if _order.sdef_order_type in [OrderType.PINGUI, OrderType.PINQU]:
                    session.close()
                    return self.write(ret_msg(code=-1, error_message="拼柜相关订单不能取消"))

                if _order.sdef_order_ship_type == 3 and not mt_peisong_id:
                    session.close()
                    return self.finish(ret_msg(code=-1, error_message="缺少参数美团内部订单号"))

                if _order.sdef_order_ship_type == 3:
                    o1 = QueryOrder(order_id, mt_peisong_id)
                    res = o1.query_order()
                    if not res:
                        session.close()
                        return self.finish(ret_msg(code=-1, error_message="取消订单失败，请稍后重试"))
                    if res["code"] != 0:
                        session.close()
                        return self.finish(ret_msg(code=-1, error_message=res["message"]))
                    mt_status = res["data"]["status"]
                    if mt_status not in [0, 20]:
                        session.close()
                        return self.finish(ret_msg(code=-1, error_message="美团外卖正在配送中不能取消"))
                    o = CancelOrder(order_id, mt_peisong_id)
                    result = o.cancel_order()
                    if not result:
                        session.close()
                        return self.finish(ret_msg(code=-1, error_message="取消订单失败，请稍后重试"))
                    if result["code"] != 0:
                        session.close()
                        return self.finish(ret_msg(code=-1, error_message=result["message"]))
                    logger.info(f"美团外卖订单取消成功: order_id={order_id}")
                _order.sdef_order_status = OrderStatus.CHECK
                session.commit()
                msg = "订单状态修改成功：order_id={},是否审核退款={}"
                logger.info(msg.format(order_id, config.CANCEL_PAID_ORDER_IS_NEED_CHECK))
                if not config.CANCEL_PAID_ORDER_IS_NEED_CHECK:
                    cancel_order_immediate_refund(order_id)
                else:
                    # 触发退款申请异步任务
                    logger.info(f"trigger order_refund_applied: order_id={order_id}")
                    tasks.order_refund_applied.delay(order_id)

            session.close()
            return self.finish(ret_msg())


class OrderDetail(ApiBaseHandler):
    async def get(self, order_id):
        class GoodsOrder(object):
            pass

        class UserAddress(object):
            pass

        class Agency(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_order", "model": GoodsOrder},
            {"db_table": "sdet_address", "model": UserAddress},
            {"db_table": "ebt_agency", "model": Agency}
        ])

        open_id = await self.get_open_id()
        filters = dict(
            sdef_order_id=order_id,
            sdef_order_is_delete=0,
            sdef_order_device_id=open_id
        )
        order = session.query(GoodsOrder).filter_by(**filters).first()
        if not order:
            session.close()
            return self.write(ret_msg(code=-1, error_message="订单不存在"))

        status_code = order.sdef_order_status
        pay_status_code = order.sdef_order_pay_status

        if status_code == OrderStatus.WAIT_PAY:
            _status = "待支付"
        elif status_code == OrderStatus.WAIT_EXPRESS:
            _status = "等待接单"
        elif status_code == OrderStatus.EXPRESSED:
            _status = "配送中"
        elif status_code == OrderStatus.CHECK:
            _status = "审核中"
        elif status_code == OrderStatus.RECEIVED:
            _status = "已送达"
        elif status_code == OrderStatus.AFTER_SALE:
            _status = "售后中"
        elif status_code == OrderStatus.COMPLETE:
            _status = "完成"
        elif status_code == OrderStatus.INVALID:
            _status = "失效"
        elif status_code == OrderStatus.REFUND:
            _status = "已退款"
        else:
            _status = "未知"

        if pay_status_code == 1:
            pay_status = "已支付"
        elif pay_status_code == 2:
            pay_status = "已退款"
            status_code = OrderStatus.REFUND
            order.sdef_order_status = OrderStatus.REFUND
        elif pay_status_code == 0:
            pay_status = "未支付"
        else:
            pay_status = "未知"

        cart_goods = json.loads(order.sdef_order_goods)
        order_express_no = []
        try:
            if order.sdef_order_express_no:
                order_express_no = json.loads(order.sdef_order_express_no)
                if order_express_no:
                    express_no_item = order_express_no[0]
                    if isinstance(express_no_item, dict):
                        order_express_no = [
                            express_no_item.get("express_number") + " 【%s】" % express_no_item.get("express_name")
                        ]
        except Exception as e:
            logger.exception(e)

        store_id = order.sdef_order_store_id
        agency = session.query(Agency).filter_by(ebf_agency_id=store_id).first()
        flag = 1
        if agency.ebf_agency_type != 3:
            flag = 0
            logger.info("门店已撤销： order_id=%s, store_id=%s" % (order.sdef_order_id, store_id))

        address = session.query(UserAddress).filter_by(sdef_address_id=order.sdef_order_address_id).first()
        address_dict = dict(
            name=address.sdef_address_name,
            phone=address.sdef_address_phone,
            province=address.sdef_address_province,
            city=address.sdef_address_city,
            county=address.sdef_address_county,
            detail=address.sdef_address_detail,
        ) if address else {}

        goods_total_fee = 0
        for c in cart_goods:
            goods_total_fee += Decimal(c["goods_price"]) * Decimal(c["count"])

        if order.sdef_order_type == OrderType.PINGUI:
            service_fee = ApiHelper.service_fee(goods_total_fee)
        else:
            service_fee = Decimal(0)
        delivery_fee = order.sdef_order_delivery_fee
        delivery_fee = Decimal(delivery_fee) if delivery_fee else Decimal(0)
        order_fee = order.sdef_order_fee
        balance_fee = order.sdef_order_balance_fee
        coupon_fee = order.sdef_order_coupon_fee
        total_fee = "%.2f" % (order_fee + balance_fee + coupon_fee)
        context = dict(
            status=_status,
            status_code=status_code,
            pay_status=pay_status,
            pay_status_code=pay_status_code,
            pay_type=order.sdef_order_pay_type,
            coupon_fee="%.2f" % coupon_fee,
            order_type=order.sdef_order_type,
            order_id=order.sdef_order_id,
            mt_peisong_id=order.sdef_order_mt_peisong_id,
            order_express_no=order_express_no,
            delivery_fee="%.2f" % delivery_fee,
            cart_goods=cart_goods,
            create_time=order.sdef_order_create_time.strftime("%m-%d %H:%M"),
            pay_time=order.sdef_order_pay_time.strftime("%Y-%m-%d %H:%M:%S") if order.sdef_order_pay_time else None,
            order_fee="%.2f" % order_fee,
            balance_fee="%.2f" % balance_fee,
            goods_total_fee="%.2f" % goods_total_fee,
            service_fee="%.2f" % service_fee,
            total_fee=total_fee,
            store_name=agency.ebf_agency_name if agency else None,
            room_id=order.sdef_order_room_id,
            store_id=store_id,
            lng=agency.ebf_agency_longitude,
            lat=agency.ebf_agency_latitude,
            store_address=agency.ebf_agency_address if agency else None,
            ship_type=order.sdef_order_ship_type,
            address=address_dict,
            flag=flag,
            message=order.sdef_order_message
        )

        session.commit()
        session.close()
        return self.finish(ret_msg(data=context))

    async def post(self, order_id):
        class GoodsOrder(object):
            pass

        class Goods(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_order", "model": GoodsOrder},
            {"db_table": "sdet_store_goods", "model": Goods},
        ])

        action = self.get_parameter("action", None)
        mt_peisong_id = self.get_parameter("mt_peisong_id", None)
        open_id = await self.get_open_id()
        _filter = dict(
            sdef_order_id=order_id,
            sdef_order_is_delete=0,
            sdef_order_device_id=open_id
        )
        _order = session.query(GoodsOrder).filter_by(**_filter).first()
        if not _order:
            session.close()
            return self.write(ret_msg(code=-1, error_message="订单不存在"))

        if action == "confirm":
            if _order.sdef_order_status != 3:
                session.close()
                ret = ret_msg(-1, error_message="只有已发货的订单才能确认收货")
                return self.finish(ret)

            if _order.sdef_order_pay_type == PayType.COUPON:
                _order.sdef_order_status = OrderStatus.COMPLETE
            else:
                if not add_mq_task(order_id, message_type=6, delay=8 * 3600):
                    logger.info(f"添加自动确认收货到任务队列失败:order_id={order_id}")
                    session.close()
                    return self.finish(ret_msg(-1, error_message="确认收货失败"))
                _order.sdef_order_status = OrderStatus.RECEIVED
            session.commit()
            session.close()
            return self.finish(ret_msg())
        elif action == "get_express":
            try:
                if _order.sdef_order_express_no:
                    express_no = json.loads(_order.sdef_order_express_no)
                    if not express_no:
                        session.close()
                        raise Exception("没有物流信息:order_id=%s" % order_id)

                    express_no_item = express_no[0]
                    if not isinstance(express_no_item, dict):
                        logger.debug(express_no_item)
                        session.close()
                        raise Exception("物流信息格式不对:order_id=%s" % order_id)

                    express_symbol = express_no_item.get("express_symbol")
                    express_number = express_no_item.get("express_number")
                    # 使用tornado的异步查询
                    http = httpclient.AsyncHTTPClient()
                    url = "https://poll.kuaidi100.com/poll/query.do"
                    param = '{"com":"' + express_symbol.lower() + '","num":"' + express_number + '"}'
                    s = ExpressHelper.sign(param)
                    data = dict(
                        customer=KUAIDI100_CUSTOMER,
                        sign=s,
                        param=param
                    )
                    logger.debug("params=%s" % data)
                    url = url_concat(url, data)
                    request = HTTPRequest(url, body=json_encode(data), method="POST", validate_cert=False)
                    logger.debug("异步请求开始")
                    result = await http.fetch(request)
                    r = result.body.decode()
                    obj = json.loads(r)
                    if obj.get("message") != "ok":
                        ret = ret_msg(-1)
                    else:
                        ret = ret_msg(data=obj.get("data"))

                    self.finish(ret)
                    session.close()
                    logger.debug("异步请求挂起，执行其它任务")
            except Exception as e:
                session.close()
                logger.exception(e)
        elif action == "get_status":
            # 获取外卖订单实时状态
            if _order.sdef_order_ship_type != 3:
                session.close()
                return self.write(ret_msg(code=-1, error_message="非外卖订单！"))

            if not mt_peisong_id:
                session.close()
                return self.write(ret_msg(code=-1, error_message="缺少参数美团内部订单号！"))
            o = QueryOrder(order_id, mt_peisong_id)
            result = o.query_order()
            if result and result["code"] == 0:
                data = result["data"]
                session.close()
                return self.write(ret_msg(data=data))
            else:
                msg = None
                if result:
                    msg = result["message"]
                logger.error(f"查询外卖订单状态失败：order_id={order_id}, err_msg={msg}")
                session.close()
                return self.write(ret_msg(code=-1, error_message=msg))


class ReceiveCoupon(ApiBaseHandler):
    def is_allow(self, order_id, open_id):
        goods_id = "523948088470142"
        if not order_id:
            return False, "缺少请求参数！"

        class Order(object):
            pass

        class CouponUser(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_order", "model": Order},
            {"db_table": "ebt_coupon_user", "model": CouponUser}
        ])

        filters = dict(
            sdef_order_id=order_id,
            sdef_order_device_id=open_id,
            sdef_order_type=OrderType.NORMAL,
            sdef_order_pay_status=PayStatus.SUCCESS,
            sdef_order_is_delete=0
        )
        filters2 = dict(
            user_id=open_id,
            coupon_id=1,
            order_id=order_id
        )
        order = session.query(Order).filter_by(**filters).first()
        cu1 = session.query(CouponUser).filter_by(**filters2).filter(CouponUser.status != 3).first()
        if not order:
            session.close()
            return False, "订单号错误"

        if (order.sdef_order_fee + order.sdef_order_balance_fee) < Decimal("139"):
            session.close()
            return False, "订单金额不符合优惠券领取条件"

        if cu1:
            session.close()
            return False, "已领取优惠券，不能重复领取"

        cart_goods = json.loads(order.sdef_order_goods)
        result = list(filter(lambda g: g["id"] == goods_id, cart_goods))
        if not result:
            session.close()
            return False, "购买I款才能领取优惠券"

        session.close()
        return True, "1"

    async def get(self):
        order_id = self.get_parameter("order_id", None)
        open_id = await self.get_open_id()
        result, msg = self.is_allow(order_id, open_id)
        count = self.is_first_buy(open_id)
        if result or msg == "已领取优惠券，不能重复领取":
            if count == 1:
                return self.write(ret_msg(data=1))
            return self.write(ret_msg(data=0))
        else:
            return self.write(ret_msg(code=-1, error_message=msg))

    async def post(self):
        order_id = self.get_parameter("order_id", None)
        open_id = await self.get_open_id()
        result, msg = self.is_allow(order_id, open_id)
        # 0=表示取消线下门店139无门槛抵扣券
        count = 0  # self.is_first_buy(open_id)
        if not result:
            if msg == "已领取优惠券，不能重复领取":
                return self.write(ret_msg())
            return self.write(ret_msg(code=-1, error_message=msg))

        class Order(object):
            pass

        class CouponUser(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_order", "model": Order},
            {"db_table": "ebt_coupon_user", "model": CouponUser}
        ])

        filters = dict(
            sdef_order_id=order_id,
            sdef_order_device_id=open_id,
            sdef_order_type=OrderType.NORMAL,
            sdef_order_pay_status=PayStatus.SUCCESS,
            sdef_order_is_delete=0
        )
        filters2 = dict(
            user_id=open_id,
            coupon_id=3,
            order_id=order_id
        )
        filters3 = dict(
            user_id=open_id,
            coupon_id=1,
            status=3,
            order_id=order_id
        )
        order = session.query(Order).filter_by(**filters).first()
        cu1 = session.query(CouponUser).filter_by(**filters2).filter(CouponUser.status == 3).first()
        cu3 = session.query(CouponUser).filter_by(**filters3).first()
        cu4 = session.query(CouponUser).filter_by(**filters2).filter(CouponUser.status != 3).first()
        now = datetime.datetime.now()
        if cu1 and cu3 and count == 1:
            cu1.status = 0
            cu1.create_time = now
            cu3.status = 0
            cu3.create_time = now
        elif cu3:
            cu3.status = 0
            cu3.create_time = now
        else:
            try:
                cu = CouponUser()
                cu.user_id = open_id
                cu.coupon_id = 1
                cu.store_id = order.sdef_order_store_id
                cu.order_id = order_id
                cu.coupon_money = 139
                cu.create_time = now
                cu.full_money = 500
                cu.is_active = 0
                session.add(cu)

                if count == 1 and not (cu1 or cu4):
                    cu2 = CouponUser()
                    cu2.user_id = open_id
                    cu2.coupon_id = 3
                    cu2.store_id = order.sdef_order_store_id
                    cu2.order_id = order_id
                    cu2.coupon_money = 139
                    cu2.create_time = now
                    cu2.is_active = 0
                    session.add(cu2)
                logger.info(f"create_time={now}, order_id={order_id}, count={count}")
            except Exception as e:
                logger.error(e)
                session.rollback()
                session.close()
                return self.write(ret_msg(code=-1, error_message="领取优惠券失败，请稍后重试"))

        session.commit()
        session.close()
        return self.write(ret_msg())


class Coupons(ApiBaseHandler):
    async def get(self):
        class CouponUser(object):
            pass

        class Coupon(object):
            pass

        session = self.get_db_session([
            {"db_table": "ebt_coupon", "model": Coupon},
            {"db_table": "ebt_coupon_user", "model": CouponUser}
        ])

        open_id = await self.get_open_id()
        status = self.get_parameter("t", None)  # 1=已使用，0=未使用，-1=已过期, 2=无效, 3=未领取
        page_index = int(self.get_parameter("page_index", 0))
        page_size = int(self.get_parameter("page_size", 10))
        page_start = page_index * page_size
        filters = dict(user_id=open_id)
        if status:
            filters["status"] = int(status)

        cus = session.query(
            CouponUser).filter_by(**filters).order_by(CouponUser.id.desc())[page_start:page_start + page_size]
        context = list()
        for cu in cus:
            id = cu.coupon_id
            cp = session.query(Coupon).filter_by(id=id).first()
            if not cp:
                logger.error(f"coupon表无记录：cp_id={id}")
                continue

            create_time = cu.create_time
            effect_date = cu.effect_date
            expiry_date = cu.expiry_date
            is_active = cu.is_active
            store_id = cu.store_id
            cp_id = cp.id
            days = cp.effective_days

            if is_active:
                if expiry_date:
                    time_range = effect_date.strftime("%Y.%m.%d") + "-" + expiry_date.strftime("%Y.%m.%d")
                else:
                    end_time = (create_time + datetime.timedelta(days=days)).replace(hour=23, minute=59, second=58)
                    cu.expiry_date = end_time
                    cu.effect_date = create_time
                    session.commit()
                    time_range = create_time.strftime("%Y.%m.%d") + "-" + end_time.strftime("%Y.%m.%d")
            else:
                if not store_id:
                    time_range = (create_time + datetime.timedelta(days=7)).strftime("%Y.%m.%d")
                else:
                    time_range = (create_time + datetime.timedelta(hours=8)).strftime("%Y.%m.%d %H:%M")

            coupon = dict(
                id=cu.id,
                coupon_money=round(float(cu.coupon_money), 2) if cu.coupon_money else 0,
                full_money=round(float(cp.full_money), 2) if cp.full_money else 0,
                order_id=cu.order_id,
                create_time=create_time.strftime("%Y-%m-%d %H:%M:%S"),
                status=cu.status,
                title=cp.title,
                type=cp_id,
                is_active=is_active,
                instruction=cp.instruction.split("|") if cp.instruction else [],
                goods_id=cp.goods_id,
                use_range=cp.use_range,
                buy_way=cp.buy_way,
                time_range=time_range
            )
            context.append(coupon)

        session.close()
        return self.write(ret_msg(data=context))


class OrderStatusCallback(ApiBaseHandler):
    async def post(self):
        delivery_id = self.get_parameter("delivery_id", None)
        mt_peisong_id = self.get_parameter("mt_peisong_id", None)
        order_id = self.get_parameter("order_id", None)
        status = self.get_parameter("status", None)
        courier_name = self.get_parameter("courier_name", None)
        courier_phone = self.get_parameter("courier_phone", None)
        cancel_reason_id = self.get_parameter("cancel_reason_id", None)
        cancel_reason = self.get_parameter("cancel_reason", None)
        appkey = self.get_parameter("appkey", None)
        timestamp = self.get_parameter("timestamp", None)
        sign = self.get_parameter("sign", None)

        if not delivery_id:
            logger.error(f"缺少参数配送活动标识: order_id={order_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数配送活动标识"))

        if not mt_peisong_id:
            logger.error(f"缺少参数内部订单id: order_id={order_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数内部订单id"))

        if not order_id:
            logger.error(f"缺少参数外部订单号: order_id={order_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数外部订单号"))

        if not status:
            logger.error(f"缺少参数状态代码: order_id={order_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数状态代码"))

        if not appkey:
            logger.error(f"缺少参数appkey: order_id={order_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数appkey"))

        if not timestamp:
            logger.error(f"缺少参数时间戳: order_id={order_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数时间戳"))

        delivery_id = int(delivery_id)
        status = int(status)
        timestamp = int(timestamp)
        if cancel_reason_id:
            cancel_reason_id = int(cancel_reason_id)

        secret = config.MEITUAN_APP_SECRET
        data = dict(
            delivery_id=delivery_id,
            mt_peisong_id=mt_peisong_id,
            order_id=order_id,
            status=status,
            courier_name=courier_name,
            courier_phone=courier_phone,
            cancel_reason_id=cancel_reason_id,
            cancel_reason=cancel_reason,
            appkey=config.MEITUAN_APP_KEY,
            timestamp=timestamp,
        )
        sign2 = SignHelper.get_sign(data, secret)
        logger.info(f"sign={sign}, sign2={sign2}, order_id={order_id}")
        if sign != sign2:
            logger.error(f"签名认证失败: sign={sign}, sign2={sign2}, order_id={order_id}")
            return self.write(ret_msg(code=-1, error_message="签名认证失败"))

        class GoodsOrder(object):
            pass

        class UserAddress(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_address", "model": UserAddress},
            {"db_table": "sdet_order", "model": GoodsOrder},
        ])

        order = session.query(GoodsOrder).filter_by(sdef_order_id=order_id).first()
        if not order:
            logger.error(f"订单不存在: order_id={order_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="订单不存在"))

        if status == 0:
            order.sdef_order_status = OrderStatus.WAIT_EXPRESS
        elif status == 20 or status == 30:
            order.sdef_order_status = OrderStatus.EXPRESSED
            order.sdef_order_courier_name = courier_name
            order.sdef_order_courier_phone = courier_phone
        elif status == 50:
            order.sdef_order_status = OrderStatus.RECEIVED
        elif status == 99:
            logger.info(f"订单已取消: order_id={order_id}, reason_id={cancel_reason_id}, reason={cancel_reason}")
            remarks = ""
            goods_text = ""
            goods_info = json.loads(order.sdef_order_goods)
            for g in goods_info:
                goods_name = g["goods_name"]
                count = Decimal(g["count"])
                goods_text += f"{goods_name} x {count} \n"
            if order.sdef_order_remarks:
                remarks = order.sdef_order_remarks
            order.sdef_order_remarks = remarks + "【美团已取消订单配送】"
            if order.sdef_order_status == OrderStatus.CHECK:
                pass
            elif order.sdef_order_pay_status != PayStatus.REFUND:
                logger.info(f"美团主动取消订单配送但门店未退款给客户：order_id={order_id}, status={order.sdef_order_status}")
                address_id = order.sdef_order_address_id
                open_id = order.sdef_order_device_id
                address = session.query(UserAddress).filter_by(sdef_address_id=address_id).first()
                province = address.sdef_address_province
                city = address.sdef_address_city
                county = address.sdef_address_county
                detail = address.sdef_address_detail
                addr = province + city + county + detail
                total_fee = "%.2f" % (order.sdef_order_fee + order.sdef_order_balance_fee +
                                      order.sdef_order_coupon_fee)
                order.sdef_order_status = OrderStatus.CHECK
                tasks.order_refund_applied.delay(order_id, cancel_reason)
                tasks.send_cancel_order_notice.delay(open_id, order_id, total_fee, goods_text, addr, cancel_reason)
            else:
                logger.info(f"订单已取消，门店已退款给用户: order_id={order_id}")
        else:
            logger.error(f"订单状态类型不存在: order_id={order_id}, status={status}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="订单状态类型不存在"))

        session.commit()
        session.close()
        return self.write(ret_msg())


class OrderExceptionCallback(ApiBaseHandler):
    async def post(self):
        delivery_id = self.get_parameter("delivery_id", None)
        mt_peisong_id = self.get_parameter("mt_peisong_id", None)
        order_id = self.get_parameter("order_id", None)
        exception_id = self.get_parameter("exception_id", None)
        exception_code = self.get_parameter("exception_code", None)
        exception_descr = self.get_parameter("exception_descr", None)
        exception_time = self.get_parameter("exception_time", None)
        courier_name = self.get_parameter("courier_name", None)
        courier_phone = self.get_parameter("courier_phone", None)
        appkey = self.get_parameter("appkey", None)
        timestamp = self.get_parameter("timestamp", None)
        sign = self.get_parameter("sign", None)

        if not delivery_id:
            logger.error(f"缺少参数配送活动标识: order_id={order_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数配送活动标识"))

        if not mt_peisong_id:
            logger.error(f"缺少参数内部订单id: order_id={order_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数内部订单id"))

        if not order_id:
            logger.error(f"缺少参数外部订单号: order_id={order_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数外部订单号"))

        if not exception_id:
            logger.error(f"缺少参数异常ID: order_id={order_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数异常ID"))

        if not exception_code:
            logger.error(f"缺少参数订单异常代码: order_id={order_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数订单异常代码"))

        if not exception_descr:
            logger.error(f"缺少参数订单异常详细信息: order_id={order_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数订单异常详细信息"))

        if not exception_time:
            logger.error(f"缺少参数上报时间: order_id={order_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数上报时间"))

        if not courier_name:
            logger.error(f"缺少参数配送员姓名: order_id={order_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数配送员姓名"))

        if not courier_phone:
            logger.error(f"缺少参数配送员电话: order_id={order_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数配送员电话"))

        if not appkey:
            logger.error(f"缺少参数appkey: order_id={order_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数appkey"))

        if not timestamp:
            logger.error(f"缺少参数时间戳: order_id={order_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数时间戳"))

        delivery_id = int(delivery_id)
        exception_id = int(exception_id)
        exception_code = int(exception_code)
        exception_time = int(exception_time)
        timestamp = int(timestamp)

        secret = config.MEITUAN_APP_SECRET
        data = dict(
            delivery_id=delivery_id,
            mt_peisong_id=mt_peisong_id,
            order_id=order_id,
            exception_id=exception_id,
            exception_code=exception_code,
            exception_descr=exception_descr,
            exception_time=exception_time,
            courier_name=courier_name,
            courier_phone=courier_phone,
            appkey=config.MEITUAN_APP_KEY,
            timestamp=timestamp,
        )
        sign2 = SignHelper.get_sign(data, secret)
        logger.info(f"sign={sign}, sign2={sign2}, order_id={order_id}")
        if sign != sign2:
            logger.error(f"签名认证失败: sign={sign}, sign2={sign2}, order_id={order_id}")
            return self.write(ret_msg(code=-1, error_message="签名认证失败"))

        class GoodsOrder(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_order", "model": GoodsOrder},
        ])

        order = session.query(GoodsOrder).filter_by(sdef_order_id=order_id).first()
        if not order:
            logger.error(f"订单不存在: order_id={order_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="订单不存在"))

        switch = {
            10001: "顾客电话关机",
            10002: "顾客电话已停机",
            10003: "顾客电话无人接听",
            10004: "顾客电话为空号",
            10005: "顾客留错电话",
            10006: "联系不上顾客其他原因",
            10101: "顾客更改收货地址",
            10201: "送货地址超区",
            10202: "顾客拒收货品",
            10203: "顾客要求延迟配送",
            10401: "商家关店/未营业",
        }
        remarks = ""
        if order.sdef_order_remarks:
            remarks = order.sdef_order_remarks

        remarks += f"【{exception_code}:{switch[exception_code]}，"
        remarks += f"{exception_descr},上报人姓名：{courier_name}, 上报人号码：{courier_phone}】"
        order.sdef_order_remarks = remarks
        session.commit()
        session.close()
        return self.write(ret_msg())


class AreaCallback(ApiBaseHandler):
    async def post(self):
        delivery_service_code = self.get_parameter("delivery_service_code", None)
        shop_id = self.get_parameter("shop_id", None)
        scope = self.get_parameter("scope", None)
        appkey = self.get_parameter("appkey", None)
        timestamp = self.get_parameter("timestamp", None)
        sign = self.get_parameter("sign", None)

        if not delivery_service_code:
            logger.error(f"缺少参数配送服务代码: shop_id={shop_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数配送服务代码"))

        if not shop_id:
            logger.error(f"缺少参数门店id: shop_id={shop_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数门店id"))

        if not scope:
            logger.error(f"缺少参数门店配送范围: shop_id={shop_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数门店配送范围"))

        if not appkey:
            logger.error(f"缺少参数appkey: shop_id={shop_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数appkey"))

        if not timestamp:
            logger.error(f"缺少参数时间戳: shop_id={shop_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数时间戳"))

        delivery_service_code = int(delivery_service_code)
        timestamp = int(timestamp)

        secret = config.MEITUAN_APP_SECRET
        data = dict(
            delivery_service_code=delivery_service_code,
            shop_id=shop_id,
            scope=scope,
            appkey=config.MEITUAN_APP_KEY,
            timestamp=timestamp,
        )
        sign2 = SignHelper.get_sign(data, secret)
        logger.info(f"sign={sign}, sign2={sign2}, shop_id={shop_id}")
        if sign != sign2:
            logger.error(f"签名认证失败: sign={sign}, sign2={sign2}, shop_id={shop_id}")
            return self.write(ret_msg(code=-1, error_message="签名认证失败"))

        now = datetime.datetime.now()
        id = f"{shop_id}{delivery_service_code}"
        db = mongodb_util.db
        result = db.sdec_store_area.find_one({"_id": id})
        if result:
            db.sdec_store_area.update({"_id": id}, {"$set": {"scope": scope, "update_time": now}})
        else:
            content = dict(
                _id=id,
                shop_id=shop_id,
                delivery_service_code=delivery_service_code,
                create_time=now,
                update_time=now,
                scope=scope
            )
            db.sdec_store_area.insert(content)
        return self.write(ret_msg())


class DeliveryRiskLevelCallback(ApiBaseHandler):
    async def post(self):
        shop_id = self.get_parameter("shop_id", None)
        delivery_risk_level = self.get_parameter("delivery_risk_level", None)  # 配送风险等级 0=正常 1-4=出现风险或无法配送
        appkey = self.get_parameter("appkey", None)
        timestamp = self.get_parameter("timestamp", None)
        sign = self.get_parameter("sign", None)

        if not shop_id:
            logger.error(f"缺少参数门店id: shop_id={shop_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数门店id"))

        if not delivery_risk_level:
            logger.error(f"缺少参数配送风险等级: shop_id={shop_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数配送风险等级"))

        if not appkey:
            logger.error(f"缺少参数appkey: shop_id={shop_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数appkey"))

        if not timestamp:
            logger.error(f"缺少参数时间戳: shop_id={shop_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数时间戳"))

        delivery_risk_level = int(delivery_risk_level)
        timestamp = int(timestamp)

        secret = config.MEITUAN_APP_SECRET
        data = dict(
            delivery_risk_level=delivery_risk_level,
            shop_id=shop_id,
            appkey=config.MEITUAN_APP_KEY,
            timestamp=timestamp,
        )
        sign2 = SignHelper.get_sign(data, secret)
        logger.info(f"sign={sign}, sign2={sign2}, shop_id={shop_id}")
        if sign != sign2:
            logger.error(f"签名认证失败: sign={sign}, sign2={sign2}, shop_id={shop_id}")
            return self.write(ret_msg(code=-1, error_message="签名认证失败"))

        logger.error(f"配送风险等级回调:shop_id={shop_id}, risk_level={delivery_risk_level}")
        return self.write(ret_msg())


class Area(ApiBaseHandler):
    async def get(self):
        delivery_service_code = self.get_parameter("delivery_service_code", None)
        shop_id = self.get_parameter("shop_id", None)
        if not delivery_service_code:
            logger.error(f"缺少参数配送服务代码: shop_id={shop_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数配送服务代码"))

        if not shop_id:
            logger.error(f"缺少参数门店id: shop_id={shop_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数门店id"))

        delivery_service_code = int(delivery_service_code)
        id = f"{shop_id}{delivery_service_code}"
        db = mongodb_util.db
        result = db.sdec_store_area.find_one({"_id": id})
        if result:
            scope = json.loads(result["scope"])
        else:
            qsa = QueryShopArea(shop_id, delivery_service_code)
            res = qsa.query_shop_area()
            if res["code"] == 0:
                scope = res["data"]["scope"]
                now = datetime.datetime.now()
                logger.info("scope=%s, type(scope)=%s" % (scope, type(scope)))
                content = dict(
                    _id=id,
                    shop_id=shop_id,
                    delivery_service_code=delivery_service_code,
                    create_time=now,
                    update_time=now,
                    scope=scope
                )
                db.sdec_store_area.insert(content)
                scope = json.loads(scope)
            else:
                err_msg = res["message"]
                logger.error(f"获取门店配送范围失败: err_msg={err_msg}, shop_id={shop_id}, code={delivery_service_code}")
                scope = []
        return self.write(ret_msg(data=scope))


class TestCreateOrder(ApiBaseHandler):
    async def create_meituan_order(self, order_id, shop_id, name, addr, phone, lng, lat, weight, goods, fee, poi_seq):
        o = CreateOrderByShop(order_id, shop_id, name, addr, phone, lng, lat, poi_seq, weight, goods, fee)
        result = o.create_shop_order()
        return result

    async def get(self):
        class GoodsOrder(object):
            pass

        class Goods(object):
            pass

        class UserAddress(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_order", "model": GoodsOrder},
            {"db_table": "sdet_store_goods", "model": Goods},
            {"db_table": "sdet_address", "model": UserAddress},
        ])

        shop_id = "test_0001"
        open_id = await self.get_open_id()
        store_id = 296
        ship_type = 3
        pay_type = 2
        ip = self.request.remote_ip
        cart_goods = [{"id": "523948088470142", "count": 2, "store_id": 296, "is_buy": 1, "room_id": "0"}]
        cart_goods_array = []
        price_total = Decimal("0")
        total_count = 0
        goods_array = []
        for c in cart_goods:
            _filter = dict(
                sdef_goods_id=c["id"],
                sdef_goods_factory_uid=config.FACTORY_UID
            )
            _goods = session.query(Goods).filter_by(**_filter).first()
            count = int(c["count"])
            total_count += count
            goods_id = c["id"]
            goods_img = json.loads(_goods.sdef_goods_thumbnail)[0]
            goods_price = _goods.sdef_goods_price
            gname = _goods.sdef_goods_name
            goods_detail = dict(
                goodCount=count,
                goodName=gname,
                goodPrice=float("%.2f" % goods_price),
                goodUnit="支"
            )
            goods_array.append(goods_detail)
            goods = dict(
                id=goods_id,
                count=count,
                goods_img=goods_img,
                goods_name=_goods.sdef_goods_name,
                goods_price=goods_price,
                goods_member_price=_goods.sdef_goods_member_price,
                goods_pin_price=_goods.sdef_goods_pin_price,
                balance_rate=_goods.sdef_goods_balance_rate,
                rate=_goods.sdef_goods_rate,
                master_rate=_goods.sdef_goods_master_rate,
                invite_rate=_goods.sdef_goods_invite_rate,
                career_rate=_goods.sdef_goods_career_rate,
                invite_career_rate=_goods.sdef_goods_invite_career_rate,
                supplier=_goods.sdef_goods_supplier,
                goods_cost=_goods.sdef_goods_cost,
                store_id=store_id,
                room_id=0
            )
            price_total += goods_price * Decimal(count)
            cart_goods_array.append(goods)

        today = datetime.date.today()
        year = today.year
        month = today.month
        day = today.day
        poi_seq = session.query(func.count(GoodsOrder.sdef_order_id)).filter(
            GoodsOrder.sdef_order_store_id > 0).filter(and_(
                extract('year', GoodsOrder.sdef_order_create_time) == year,
                extract('month', GoodsOrder.sdef_order_create_time) == month,
                extract('day', GoodsOrder.sdef_order_create_time) == day)).scalar() + 1
        table_id = "%04d" % poi_seq

        default_address = AddressHelper.get_default(open_id)
        use_address = AddressHelper.get_use(open_id)
        db = mongodb_util.db
        address = None
        if use_address:
            logger.debug("使用地址: address_id=%s" % use_address["address_id"])
            address = session.query(UserAddress).filter_by(sdef_address_id=use_address["address_id"]).first()
        elif default_address:
            logger.debug("默认地址: address_id=%s" % default_address["address_id"])
            address = session.query(UserAddress).filter_by(sdef_address_id=default_address["address_id"]).first()
        else:
            result = db.sdec_address.find({"user_id": open_id, "is_delete": False}).sort("update_time",
                                                                                         pymongo.DESCENDING)
            for a in result:
                address = session.query(UserAddress).filter_by(sdef_address_id=a["address_id"]).first()
                if address:
                    break

        now = datetime.datetime.now()
        goods_order = GoodsOrder()
        order_id = generator_trade_no()
        goods_order.sdef_order_id = order_id
        goods_order.sdef_order_factory_uid = config.FACTORY_UID
        goods_order.sdef_order_device_id = open_id
        goods_order.sdef_order_fee = 0
        goods_order.sdef_order_goods = json.dumps(cart_goods_array, cls=JSONEncoder)
        goods_order.sdef_order_balance_fee = 0
        goods_order.sdef_order_name = "思达尔线下门店订单"
        goods_order.sdef_order_store_id = store_id
        goods_order.sdef_order_room_id = table_id
        goods_order.sdef_order_ship_type = ship_type
        goods_order.sdef_order_coupon_fee = 0
        goods_order.sdef_order_address_id = address.sdef_address_id
        goods_order.sdef_order_status = OrderStatus.WAIT_EXPRESS
        goods_order.sdef_order_pay_status = PayStatus.SUCCESS
        goods_order.sdef_order_pay_time = now
        goods_order.sdef_order_pay_type = pay_type
        goods_order.sdef_order_type = OrderType.NORMAL
        goods_order.sdef_order_ip = ip
        goods_order.sdef_order_delivery_fee = 0
        goods_order.sdef_order_create_time = now
        goods_order.sdef_order_agency_uid = config.DEFAULT_AGENCY_UID
        goods_order.sdef_order_message = "美团外卖测试订单"

        name = address.sdef_address_name
        phone = address.sdef_address_phone
        weight = float("%.2f" % (0.75 * total_count))
        fee = float("%.2f" % price_total)
        g_detail = dict(goods=goods_array)
        g_detail = json.dumps(g_detail)
        province = address.sdef_address_province
        city = address.sdef_address_city
        county = address.sdef_address_county
        address_detail = address.sdef_address_detail
        addr = province + city + county + address_detail
        addr = addr.replace(' ', '')
        try:
            lng, lat = ApiHelper.geocode(addr)
            lng = float(lng)
            lat = float(lat)
        except Exception as e:
            session.close()
            logger.error(f"获取经纬度失败: err={e}")
            return self.write(ret_msg(code=-1, error_message="获取经纬度失败"))

        res = await self.create_meituan_order(order_id, shop_id, name, addr, phone,
                                              lng, lat, weight, g_detail, fee, table_id)
        if res and res["code"] == 0:
            mt_peisong_id = res["data"]["mt_peisong_id"]
            goods_order.sdef_order_mt_peisong_id = mt_peisong_id
            logger.info(f"创建美团订单成功:order_id={order_id}, mt_peisong_id={mt_peisong_id}")
        else:
            msg = None
            if res:
                msg = res["message"]
            logger.error(f"创建美团内部订单失败:order_id={order_id}, err_msg={msg}")
        session.add(goods_order)
        session.commit()
        session.close()
        # 8小时之后自动确认收货
        if not add_mq_task(order_id, message_type=6, delay=8 * 3600):
            logger.info(f"8小时后自动确认收货加入任务队列失败：out_trade_no={out_trade_no}")
        tasks.cloud_print_order.delay(order_id)
        return self.write(ret_msg())


class ShopCheck(ApiBaseHandler):
    async def post(self):
        delivery_service_code = self.get_parameter("delivery_service_code", None)
        shop_id = self.get_parameter("shop_id", None)
        address = self.get_parameter("address", None)
        lng = self.get_parameter("lng", None)
        lat = self.get_parameter("lat", None)
        if not delivery_service_code:
            logger.error(f"缺少参数配送服务代码: shop_id={shop_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数配送服务代码"))

        if not shop_id:
            logger.error(f"缺少参数门店id: shop_id={shop_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数门店id"))

        if not address:
            logger.error(f"缺少参数收件人地址: shop_id={shop_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数收件人地址"))

        if not lng:
            logger.error(f"缺少参数收件人经度: shop_id={shop_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数收件人经度"))

        if not lat:
            logger.error(f"缺少参数收件人纬度: shop_id={shop_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数收件人纬度"))

        lng = float(lng)
        lat = float(lat)
        co = CheckOrder(shop_id, address, lng, lat, delivery_service_code)
        res = co.check_order()
        if not res:
            logger.error(f"配送能力校验失败: shop_id={shop_id}")
            return self.finish(ret_msg(code=-1, error_message="配送能力校验失败"))

        if res["code"] != 0:
            msg = res["message"]
            logger.error(f"配送能力校验失败: shop_id={shop_id}, err_msg={msg}")
            return self.finish(ret_msg(code=-1, error_message=msg))

        data = res["message"]
        return self.finish(ret_msg(data=data))


class GainRiderLocation(ApiBaseHandler):
    async def post(self):
        delivery_id = self.get_parameter("order_id", None)
        mt_peisong_id = self.get_parameter("mt_peisong_id", None)
        if not delivery_id:
            logger.error("缺少参数配送活动标识")
            return self.write(ret_msg(code=-1, error_message="缺少参数配送活动标识"))

        if not mt_peisong_id:
            logger.error("缺少参数美团配送内部订单id")
            return self.write(ret_msg(code=-1, error_message="缺少参数美团配送内部订单id"))

        grl = GetRiderLocation(delivery_id, mt_peisong_id)
        res = grl.get_rider_location()
        if not res:
            logger.error(f"获取骑手位置失败: delivery_id={delivery_id}")
            return self.finish(ret_msg(code=-1, error_message="配送能力校验失败"))

        if res["code"] != 0:
            msg = res["message"]
            logger.error(f"获取骑手位置失败: delivery_id={delivery_id}, err_msg={msg}")
            return self.finish(ret_msg(code=-1, error_message=msg))

        data = res["data"]
        data["lat"] /= 10 ** 6
        data["lng"] /= 10 ** 6
        return self.finish(ret_msg(data=data))


class CommentProduct(ApiBaseHandler):
    async def post(self):
        open_id = await self.get_open_id()
        order_id = self.get_parameter("order_id", None)
        delivery_star = int(self.get_parameter("delivery_star", 5))
        store_star = int(self.get_parameter("store_star", 5))
        comment_info = self.get_parameter("comment_info", None)
        is_anonymous = int(self.get_parameter("is_anonymous", 0))

        if not order_id:
            logger.error(f"缺少参数order_id: open_id={open_id}")
            return self.write(ret_msg(code=-1, error_message="缺少参数订单编号"))

        class GoodsOrder:
            pass

        class User(object):
            pass

        session = self.get_db_session([
            {"db_table": "sdet_order", "model": GoodsOrder},
            {"db_table": "ebt_user", "model": User},
        ])

        order = session.query(GoodsOrder).filter_by(sdef_order_id=order_id).first()
        if not order:
            logger.error(f"参数错误: order_id={order_id} open_id={open_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="请求参数订单编号错误"))

        if order.sdef_order_device_id != open_id:
            logger.error(f"该订单不属于用户: order_id={order_id} open_id={open_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="用户自己支付的订单才能参与评论"))

        if order.sdef_order_status != OrderStatus.COMPLETE:
            logger.error(f"订单没有完成: order_id={order_id} open_id={open_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="只有已完成的订单才能评论"))

        if order.sdef_order_pay_status != PayStatus.SUCCESS:
            logger.error(f"订单未支付: order_id={order_id} open_id={open_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="订单未支付"))

        if order.sdef_order_is_comment:
            logger.error(f"订单已评价: order_id={order_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="订单已评价"))

        if not isinstance(comment_info, list):
            logger.info("type(comment_info)=%s" % type(comment_info))
            comment_info = json.loads(comment_info)

        # 订单类型（1=普通订单，2=送礼订单，3=加盟费订单，4=特惠订单，5=餐券抵扣订单，6=礼品卡订单，7=新礼品订单，8=体验卡买单订单）
        if order.sdef_order_type in [3, 5, 6, 8]:
            logger.error(f"该订单不支持评价: order_id={order_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="该订单不支持评价"))

        user = session.query(User).filter_by(ebf_user_id=open_id).first()
        headurl = user.ebf_user_headurl
        nickname = "匿名用户" if is_anonymous else user.ebf_user_nickname
        store_id = order.sdef_order_store_id
        now = datetime.datetime.now()
        user_info = dict(
            open_id=open_id,
            nickname=nickname,
            headurl=headurl
        )
        order_goods = order.sdef_order_goods
        for c in comment_info:
            if c.get("goods_id", None) not in order_goods:
                logger.error(f"订单没有此商品: order_id={order_id}")
                return self.write(ret_msg(code=-1, error_message="商品编号错误"))

        insert_data = list()
        for c in comment_info:
            insert_data.append(dict(
                _id=generator_trade_no(),  # 评论ID 主键
                user=user_info,  # 评价人信息
                goods_id=c["goods_id"],  # 商品ID
                star=c["star"],  # 商品评论分数(取值1-5: 大于3好评，小于3差评，等于3中评，默认好评)
                imgs=c["imgs"],  # 图片地址列表
                content=c["content"],  # 内容
                comments=[],  # 评论
                upvotes=[],  # 点赞用户信息
                comment_nums=0,  # 评论数
                upvote_nums=0,  # 点赞数
                create_time=now,  # 创建时间
                order_id=order_id,  # 订单编号
                store_id=store_id,  # 门店编号
                delivery_star=delivery_star,  # 物流星级评价
                store_star=store_star,  # 商家星级评价
                is_delete=0,  # 是否删除
                is_sys_default_comment=0,  # 是否系统默认评价(0=否， 1=是)
                is_anonymous=is_anonymous  # 是否匿名评价
            ))

        try:
            db = mongodb_util.db
            db.sdec_product_comment.insert_many(insert_data)
        except Exception as e:
            logger.error(e)
            session.close()
            return self.write(ret_msg(code=-1, error_message="评论失败, 请稍后重试"))

        order.sdef_order_is_comment = 1
        session.commit()
        session.close()
        return self.write(ret_msg())


class GoodsComments(ApiBaseHandler):
    async def get(self):
        open_id = await self.get_open_id()
        goods_id = self.get_argument("goods_id", None)
        page_index = int(self.get_argument("page_index", 0))
        page_size = int(self.get_argument("page_size", 10))
        ApiHelper.inspect_common_params(page_index, page_size)
        page_start = page_index * page_size
        goods_comments = []
        try:
            db = mongodb_util.db
            if goods_id:
                # 查看指定商品的评价
                result = db.sdec_product_comment.find({
                    "is_delete": {"$ne": 1},
                    "goods_id": goods_id
                }).sort("_id", -1).skip(page_start).limit(page_size)
                # 商品的评价总数
                goods_comments_count = db.sdec_product_comment.find({
                    "is_delete": {"$ne": 1},
                    "goods_id": goods_id
                }).count()
            else:
                # 查看用户自己的评价
                result = db.sdec_product_comment.find({
                    "is_delete": {"$ne": 1},
                    "user.open_id": open_id
                }).sort("_id", -1).skip(page_start).limit(page_size)
                # 商品的评价总数
                goods_comments_count = db.sdec_product_comment.find({
                    "is_delete": {"$ne": 1},
                    "user.open_id": open_id
                }).count()
            for c in result:
                c["create_time"] = c["create_time"].strftime("%Y年%m月%d日 %H:%M:%S")
                # 判断用户是否对该饭局已点赞
                if open_id in [u["open_id"] for u in c["upvotes"]]:
                    # 已点赞
                    is_upvote = 1
                else:
                    # 未点赞
                    is_upvote = 0

                # 判断用户是否有删除帖子权限
                if open_id == c["user"]["open_id"] or open_id in config.POSTS_DELETE_PERMISSION:
                    is_delete_permission = 1
                else:
                    is_delete_permission = 0
                c.pop("comments")
                c.pop("upvotes")
                c["is_delete_permission"] = is_delete_permission
                c["goods_comments_count"] = goods_comments_count
                c["is_upvote"] = is_upvote
                goods_comments.append(c)
        except Exception as e:
            logger.error(e)
            return self.write(ret_msg(code=-1, error_message="获取评价列表失败, 请稍后重试"))

        return self.write(ret_msg(data=goods_comments))


class DeleteComments(ApiBaseHandler):
    async def post(self):
        open_id = await self.get_open_id()
        comment_id = self.get_parameter("id", None)
        if not comment_id:
            return self.write(ret_msg(code=-1, error_message="缺少请求参数商品评价ID"))

        try:
            db = mongodb_util.db
            comment = db.sdec_product_comment.find_one({"_id": comment_id})
            if not comment:
                return self.write(ret_msg(code=-1, error_message="评价不存在!"))

            # 判断用户是否有删除评价权限
            if open_id != comment["user"]["open_id"] and open_id not in config.POSTS_DELETE_PERMISSION:
                return self.write(ret_msg(code=-1, error_message="权限不足!"))
            result = db.sdec_product_comment.delete_one({"_id": comment_id})
            count = result.deleted_count
        except Exception as e:
            logger.error(e)
            return self.write(ret_msg(code=-1, error_message="删除失败!"))

        return self.write(ret_msg(data=count))


class CommentUpvote(ApiBaseHandler):
    async def post(self):
        open_id = await self.get_open_id()
        comment_id = self.get_parameter("id", None)
        if not comment_id:
            return self.write(ret_msg(code=-1, error_message="缺少请求参数商品评价ID"))

        try:
            db = mongodb_util.db
            result = db.sdec_product_comment.find_one({"_id": comment_id})
            if not result:
                return self.write(ret_msg(code=-1, error_message="商品评价ID错误"))
        except Exception as e:
            logger.error(e)
            return self.write(ret_msg(code=-1, error_message="系统繁忙, 请稍后重试"))

        class User(object):
            pass

        session = self.get_db_session([
            {"db_table": "ebt_user", "model": User},
        ])

        user = session.query(User).filter_by(ebf_user_id=open_id).first()
        if not user:
            logger.error(f"用户表无记录: open_id={open_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="用户表无记录"))

        headurl = user.ebf_user_headurl
        nickname = user.ebf_user_nickname
        user_info = dict(
            open_id=open_id,
            nickname=nickname,
            headurl=headurl
        )
        upvotes = result["upvotes"]
        upvote_nums = result["upvote_nums"]
        if open_id in [u["open_id"] for u in upvotes]:
            # 已点赞
            upvote_index = [u["open_id"] for u in upvotes].index(open_id)
            upvotes.pop(upvote_index)
            upvote_nums -= 1
            is_upvote = 0
        else:
            # 未点赞
            upvotes.append(user_info)
            upvote_nums += 1
            is_upvote = 1

        try:
            db.sdec_product_comment.update_one(
                {"_id": comment_id},
                {"$set": {"upvote_nums": upvote_nums, "upvotes": upvotes}}
            )
        except Exception as e:
            logger.error(e)
            session.close()
            return self.write(ret_msg(code=-1, error_message="系统繁忙, 请稍后重试"))

        context = dict(upvote_nums=upvote_nums, is_upvote=is_upvote)
        session.close()
        return self.write(ret_msg(data=context))


class AvailableCoupons(ApiBaseHandler):
    async def post(self):
        open_id = await self.get_open_id()
        total_price = Decimal(self.get_parameter("total_price", 0))
        able_use_balance_goods_fee = Decimal(self.get_parameter("able_use_balance_goods_fee", 0))
        goods_list = self.get_parameter("goods_list", [])
        order_type = int(self.get_parameter("order_type", 1))
        store_id = int(self.get_parameter("store_id", 0))
        page_index = int(self.get_parameter("page_index", 0))
        page_size = int(self.get_parameter("page_size", 10))
        ApiHelper.inspect_common_params(page_index, page_size)
        page_start = page_index * page_size

        if not isinstance(goods_list, list):
            goods_list = json.loads(goods_list)

        # 兼容旧参数处理
        if not total_price:
            total_price = able_use_balance_goods_fee

        context = get_available_coupons(store_id, order_type, open_id, total_price,
                                        goods_list, page_size, page_start)
        return self.write(ret_msg(data=context))
